View Javadoc

1   /*
2    * $Id: MuleUniversalChannel.java 7963 2007-08-21 08:53:15Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.providers.soap.xfire.transport;
12  
13  import org.mule.config.MuleProperties;
14  import org.mule.impl.MuleEvent;
15  import org.mule.impl.MuleMessage;
16  import org.mule.impl.RequestContext;
17  import org.mule.impl.endpoint.MuleEndpoint;
18  import org.mule.providers.http.HttpConnector;
19  import org.mule.providers.http.HttpConstants;
20  import org.mule.providers.streaming.StreamMessageAdapter;
21  import org.mule.umo.UMOEvent;
22  import org.mule.umo.UMOException;
23  import org.mule.umo.UMOMessage;
24  import org.mule.umo.endpoint.UMOEndpoint;
25  import org.mule.umo.provider.OutputHandler;
26  import org.mule.umo.provider.UMOStreamMessageAdapter;
27  
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.util.HashMap;
32  import java.util.Iterator;
33  import java.util.Map;
34  
35  import javax.activation.DataHandler;
36  import javax.mail.MessagingException;
37  import javax.xml.stream.XMLStreamException;
38  import javax.xml.stream.XMLStreamWriter;
39  
40  import org.apache.commons.io.output.ByteArrayOutputStream;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.codehaus.xfire.MessageContext;
44  import org.codehaus.xfire.XFireException;
45  import org.codehaus.xfire.XFireRuntimeException;
46  import org.codehaus.xfire.attachments.Attachments;
47  import org.codehaus.xfire.attachments.ByteDataSource;
48  import org.codehaus.xfire.attachments.JavaMailAttachments;
49  import org.codehaus.xfire.attachments.SimpleAttachment;
50  import org.codehaus.xfire.exchange.AbstractMessage;
51  import org.codehaus.xfire.exchange.InMessage;
52  import org.codehaus.xfire.exchange.OutMessage;
53  import org.codehaus.xfire.soap.Soap12;
54  import org.codehaus.xfire.soap.SoapConstants;
55  import org.codehaus.xfire.soap.SoapVersion;
56  import org.codehaus.xfire.transport.AbstractChannel;
57  import org.codehaus.xfire.transport.Channel;
58  import org.codehaus.xfire.transport.Transport;
59  import org.codehaus.xfire.util.STAXUtils;
60  
61  /**
62   * The MuleUniversalChannel is an XFire Channel implementation that uses
63   * a Mule Transport under the covers. It theoretically can use any Mule transport
64   * but only transports that support streaming can be used with XFire.
65   *
66   * This channel is used for making Soap requests using XFire, not receiving them.
67   *
68   */
69  public class MuleUniversalChannel extends AbstractChannel
70  {
71      /**
72       * logger used by this class
73       */
74      protected final transient Log logger = LogFactory.getLog(getClass());
75  
76      public MuleUniversalChannel(String uri, Transport transport)
77      {
78          setTransport(transport);
79          setUri(uri);
80      }
81  
82      public void open()
83      {
84          // nothing to do here
85      }
86  
87      public void send(MessageContext context, OutMessage message) throws XFireException
88      {
89          if (message.getUri().equals(Channel.BACKCHANNEL_URI))
90          {
91              final OutputStream out = (OutputStream)context.getProperty(Channel.BACKCHANNEL_URI);
92              if (out != null)
93              {
94                  final XMLStreamWriter writer = STAXUtils.createXMLStreamWriter(out, message.getEncoding(),
95                      context);
96  
97                  message.getSerializer().writeMessage(message, writer, context);
98              }
99              else
100             {
101                 throw new XFireRuntimeException("No backchannel exists for message");
102             }
103 
104             try
105             {
106                 Attachments atts = message.getAttachments();
107                 if (atts != null && atts.size() > 0)
108                 {
109                     writeAttachmentBody(context, message);
110                     // TODO response.setContentType(atts.getContentType());
111                     atts.write(out);
112                 }
113                 else
114                 {
115                     // TODO response.setContentType(getSoapMimeType(message));
116                     writeWithoutAttachments(context, message, out);
117                 }
118             }
119             catch (IOException e)
120             {
121                 throw new XFireException("Couldn't send message.", e);
122             }
123         }
124         else
125         {
126             try
127             {
128                 sendViaClient(context, message);
129             }
130             catch (Exception e)
131             {
132                 throw new XFireException("Failed to Send via MuleUniversalChannel: " + e.getMessage(), e);
133             }
134         }
135     }
136 
137     void writeWithoutAttachments(MessageContext context, OutMessage message, OutputStream out)
138         throws XFireException
139     {
140         XMLStreamWriter writer = STAXUtils.createXMLStreamWriter(out, message.getEncoding(), context);
141 
142         message.getSerializer().writeMessage(message, writer, context);
143 
144         try
145         {
146             writer.flush();
147         }
148         catch (XMLStreamException e)
149         {
150             logger.error(e);
151             throw new XFireException("Couldn't send message.", e);
152         }
153     }
154 
155     void writeAttachmentBody(MessageContext context, OutMessage message) throws XFireException
156     {
157         ByteArrayOutputStream bos = new ByteArrayOutputStream();
158         writeWithoutAttachments(context, message, bos);
159 
160         Attachments atts = message.getAttachments();
161 
162         ByteDataSource ds = new ByteDataSource(bos.toByteArray());
163         ds.setContentType(getSoapMimeType(message));
164         DataHandler dh = new DataHandler(ds);
165 
166         SimpleAttachment att = new SimpleAttachment("soap-message.xml", dh);
167 
168         atts.setSoapMessage(att);
169     }
170 
171     String getMimeType(AbstractMessage msg)
172     {
173         Attachments atts = msg.getAttachments();
174 
175         if (atts != null && atts.size() > 0)
176         {
177             return atts.getContentType();
178         }
179         else
180         {
181             return getSoapMimeType(msg);
182         }
183     }
184 
185     static String getSoapMimeType(AbstractMessage msg)
186     {
187         SoapVersion soap = msg.getSoapVersion();
188         String encoding = msg.getEncoding();
189         StringBuffer soapMimeType = new StringBuffer(40);
190 
191         if (soap instanceof Soap12)
192         {
193             soapMimeType.append("application/soap+xml; charset=");
194         }
195         else
196         {
197             // SOAP 1.1 & default
198             soapMimeType.append("text/xml; charset=");
199         }
200 
201         return soapMimeType.append(encoding).toString();
202     }
203 
204     private void sendViaClient(final MessageContext context, final OutMessage message) throws Exception
205     {
206         OutputHandler handler = new OutputHandler()
207         {
208             public void write(UMOEvent event, OutputStream out) throws IOException
209             {
210                 try
211                 {
212                     Attachments atts = message.getAttachments();
213                     if (atts != null && atts.size() > 0)
214                     {
215                         atts.write(out);
216                     }
217                     else
218                     {
219                         XMLStreamWriter writer = STAXUtils.createXMLStreamWriter(out, message.getEncoding(),
220                             context);
221                         message.getSerializer().writeMessage(message, writer, context);
222                         try
223                         {
224                             writer.flush();
225                         }
226                         catch (XMLStreamException e)
227                         {
228                             logger.error(e);
229                             throw new XFireException("Couldn't send message.", e);
230                         }
231                     }
232                 }
233                 catch (XFireException e)
234                 {
235                     logger.error("Couldn't send message.", e);
236                     throw new IOException(e.getMessage());
237                 }
238             }
239 
240             public Map getHeaders(UMOEvent event)
241             {
242                 Map headers = new HashMap();
243                 headers.put(HttpConstants.HEADER_CONTENT_TYPE, getSoapMimeType(message));
244                 headers.put(SoapConstants.SOAP_ACTION, message.getProperty(SoapConstants.SOAP_ACTION));
245                 UMOMessage msg = event.getMessage();
246                 for (Iterator iterator = msg.getPropertyNames().iterator(); iterator.hasNext();)
247                 {
248                     String headerName = (String)iterator.next();
249                     Object headerValue = msg.getStringProperty(headerName, null);
250 
251                     // let us filter only MULE properties except MULE_USER,
252                     // Content-Type and Content-Lenght; all other properties are
253                     // allowed through including custom headers
254                     if ((!headerName.startsWith(MuleProperties.PROPERTY_PREFIX) || (MuleProperties.MULE_USER_PROPERTY.compareTo(headerName) == 0))
255                         && (!HttpConstants.HEADER_CONTENT_TYPE.equalsIgnoreCase(headerName))
256                         && (!HttpConstants.HEADER_CONTENT_LENGTH.equalsIgnoreCase(headerName)))
257                     {
258                         headers.put(headerName, headerValue);
259                     }
260                 }
261 
262                 return headers;
263             }
264         };
265 
266         // We can create a generic StreamMessageAdapter here as the underlying
267         // transport will create one specific to the transport
268         UMOStreamMessageAdapter sp = new StreamMessageAdapter(handler);
269         sp.setProperty(HttpConnector.HTTP_METHOD_PROPERTY, HttpConstants.METHOD_POST);
270 
271         // set all properties on the message adapter
272         UMOMessage msg = RequestContext.getEvent().getMessage();
273         for (Iterator i = msg.getPropertyNames().iterator(); i.hasNext();)
274         {
275             String propertyName = (String)i.next();
276             sp.setProperty(propertyName, msg.getProperty(propertyName));
277         }
278 
279         UMOStreamMessageAdapter result = null;
280 
281         try
282         {
283             result = sendStream(getUri(), sp);
284             if (result != null)
285             {
286                 InMessage inMessage;
287                 String contentType = sp.getStringProperty(HttpConstants.HEADER_CONTENT_TYPE, "text/xml");
288                 InputStream in = result.getInputStream();
289                 if (contentType.toLowerCase().indexOf("multipart/related") != -1)
290                 {
291                     try
292                     {
293                         Attachments atts = new JavaMailAttachments(in, contentType);
294                         InputStream msgIs = atts.getSoapMessage().getDataHandler().getInputStream();
295                         inMessage = new InMessage(STAXUtils.createXMLStreamReader(msgIs,
296                             message.getEncoding(), context), getUri());
297                         inMessage.setAttachments(atts);
298                     }
299                     catch (MessagingException e)
300                     {
301                         throw new IOException(e.getMessage());
302                     }
303                 }
304                 else
305                 {
306                     inMessage = new InMessage(STAXUtils.createXMLStreamReader(in, message.getEncoding(),
307                         context), getUri());
308                 }
309                 getEndpoint().onReceive(context, inMessage);
310             }
311         }
312         finally
313         {
314             sp.release();
315             if (result != null)
316             {
317                 result.release();
318             }
319         }
320     }
321 
322     public void close()
323     {
324         // nothing to do here
325     }
326 
327     public boolean isAsync()
328     {
329         return false;
330     }
331 
332     protected UMOStreamMessageAdapter sendStream(String uri, UMOStreamMessageAdapter sa) throws UMOException
333     {
334 
335         UMOEndpoint ep = MuleEndpoint.getOrCreateEndpointForUri(uri, UMOEndpoint.ENDPOINT_TYPE_SENDER);
336         ep.setStreaming(true);
337         UMOMessage message = new MuleMessage(sa);
338         UMOEvent event = new MuleEvent(message, ep, RequestContext.getEventContext().getSession(), true);
339         UMOMessage result = ep.send(event);
340         if (result != null)
341         {
342             if (result.getAdapter() instanceof UMOStreamMessageAdapter)
343             {
344                 return (UMOStreamMessageAdapter) result.getAdapter();
345             }
346             else
347             {
348                 // TODO i18n (though this case should never happen...)
349                 throw new IllegalStateException(
350                         "Mismatch of stream states. A stream was used for outbound channel, but a stream was not used for the response");
351             }
352         }
353         return null;
354     }
355 
356 }