View Javadoc

1   /*
2    * $Id: MuleLocalChannel.java 7976 2007-08-21 14:26:13Z 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.MuleException;
14  import org.mule.impl.message.ExceptionPayload;
15  import org.mule.providers.soap.xfire.XFireConnector;
16  import org.mule.umo.UMOEventContext;
17  import org.mule.umo.UMOException;
18  import org.mule.umo.UMOMessage;
19  import org.mule.umo.manager.UMOWorkManager;
20  import org.mule.util.StringUtils;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.io.PipedInputStream;
26  import java.io.PipedOutputStream;
27  import java.io.Reader;
28  import java.io.StringReader;
29  import java.io.UnsupportedEncodingException;
30  
31  import javax.resource.spi.work.Work;
32  import javax.resource.spi.work.WorkException;
33  import javax.xml.stream.XMLStreamReader;
34  import javax.xml.stream.XMLStreamWriter;
35  
36  import edu.emory.mathcs.backport.java.util.concurrent.Semaphore;
37  import org.apache.commons.io.output.ByteArrayOutputStream;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.codehaus.xfire.MessageContext;
41  import org.codehaus.xfire.XFire;
42  import org.codehaus.xfire.XFireException;
43  import org.codehaus.xfire.XFireRuntimeException;
44  import org.codehaus.xfire.exchange.AbstractMessage;
45  import org.codehaus.xfire.exchange.InMessage;
46  import org.codehaus.xfire.exchange.OutMessage;
47  import org.codehaus.xfire.service.Service;
48  import org.codehaus.xfire.soap.SoapConstants;
49  import org.codehaus.xfire.transport.AbstractChannel;
50  import org.codehaus.xfire.transport.Channel;
51  import org.codehaus.xfire.transport.Session;
52  import org.codehaus.xfire.transport.Transport;
53  import org.codehaus.xfire.util.STAXUtils;
54  
55  /**
56   * TODO document
57   */
58  public class MuleLocalChannel extends AbstractChannel
59  {
60      protected static final String SENDER_URI = "senderUri";
61      protected static final String OLD_CONTEXT = "urn:xfire:transport:local:oldContext";
62      
63      /**
64       * logger used by this class
65       */
66      protected transient Log logger = LogFactory.getLog(getClass());
67  
68      private final Session session;
69      protected UMOWorkManager workManager;
70  
71      public MuleLocalChannel(String uri, Transport transport, Session session)
72      {
73          this.session = session;
74          setUri(uri);
75          setTransport(transport);
76      }
77  
78      public void open()
79      {
80          // template method
81      }
82  
83      public void send(final MessageContext context, final OutMessage message) throws XFireException
84      {
85          if (message.getUri().equals(Channel.BACKCHANNEL_URI))
86          {
87              final OutputStream out = (OutputStream)context.getProperty(Channel.BACKCHANNEL_URI);
88              if (out != null)
89              {
90                  final XMLStreamWriter writer = STAXUtils.createXMLStreamWriter(out, message.getEncoding(),
91                      context);
92  
93                  message.getSerializer().writeMessage(message, writer, context);
94              }
95              else
96              {
97                  MessageContext oldContext = (MessageContext)context.getProperty(OLD_CONTEXT);
98  
99                  sendViaNewChannel(context, oldContext, message, (String)context.getProperty(SENDER_URI));
100             }
101         }
102         else
103         {
104             MessageContext receivingContext = new MessageContext();
105             receivingContext.setXFire(context.getXFire());
106             receivingContext.setService(getService(context.getXFire(), message.getUri()));
107             receivingContext.setProperty(OLD_CONTEXT, context);
108             receivingContext.setProperty(SENDER_URI, getUri());
109             receivingContext.setSession(session);
110 
111             sendViaNewChannel(context, receivingContext, message, message.getUri());
112         }
113     }
114 
115     protected Service getService(XFire xfire, String uri) throws XFireException
116     {
117         if (null == xfire)
118         {
119             logger.warn("No XFire instance in context, unable to determine service");
120             return null;
121         }
122 
123         int i = uri.indexOf("//");
124 
125         if (i == -1)
126         {
127             throw new XFireException("Malformed service URI");
128         }
129 
130         String name = uri.substring(i + 2);
131         Service service = xfire.getServiceRegistry().getService(name);
132 
133         if (null == service)
134         {
135             // TODO this should be an exception...
136             logger.warn("Unable to locate '" + name + "' in ServiceRegistry");
137         }
138 
139         return service;
140     }
141 
142     private void sendViaNewChannel(final MessageContext context,
143                                    final MessageContext receivingContext,
144                                    final OutMessage message,
145                                    final String uri) throws XFireException
146     {
147         try
148         {
149             Channel channel;
150             PipedInputStream stream = new PipedInputStream();
151             PipedOutputStream outStream = new PipedOutputStream(stream);
152             try
153             {
154                 channel = getTransport().createChannel(uri);
155             }
156             catch (Exception e)
157             {
158                 throw new XFireException("Couldn't create channel.", e);
159             }
160 
161             Semaphore s = new Semaphore(2);
162             try
163             {
164                 getWorkManager().scheduleWork(new WriterWorker(outStream, message, context, s));
165                 getWorkManager().scheduleWork(
166                     new ReaderWorker(stream, message, channel, uri, receivingContext, s));
167             }
168             catch (WorkException e)
169             {
170                 throw new XFireException("Couldn't schedule worker threads. " + e.getMessage(), e);
171             }
172 
173             try
174             {
175                 s.acquire();
176             }
177             catch (InterruptedException e)
178             {
179                 // ignore is ok
180             }
181         }
182         catch (IOException e)
183         {
184             throw new XFireRuntimeException("Couldn't create stream.", e);
185         }
186     }
187 
188     public void close()
189     {
190         // template method
191     }
192 
193     public boolean isAsync()
194     {
195         return true;
196     }
197 
198     public UMOWorkManager getWorkManager()
199     {
200         return workManager;
201     }
202 
203     public void setWorkManager(UMOWorkManager workManager)
204     {
205         this.workManager = workManager;
206     }
207 
208     private class ReaderWorker implements Work
209     {
210 
211         private InputStream stream;
212         private OutMessage message;
213         private Channel channel;
214         private String uri;
215         private MessageContext context;
216         private Semaphore semaphore;
217 
218         public ReaderWorker(InputStream stream,
219                             OutMessage message,
220                             Channel channel,
221                             String uri,
222                             MessageContext context,
223                             Semaphore semaphore)
224         {
225             this.stream = stream;
226             this.message = message;
227             this.channel = channel;
228             this.uri = uri;
229             this.context = context;
230             this.semaphore = semaphore;
231         }
232 
233         public void run()
234         {
235             try
236             {
237                 final XMLStreamReader reader = STAXUtils.createXMLStreamReader(stream, message.getEncoding(),
238                     context);
239                 final InMessage inMessage = new InMessage(reader, uri);
240                 inMessage.setEncoding(message.getEncoding());
241 
242                 channel.receive(context, inMessage);
243 
244                 reader.close();
245                 stream.close();
246             }
247             catch (Exception e)
248             {
249                 throw new XFireRuntimeException("Couldn't read stream.", e);
250             }
251             finally
252             {
253                 semaphore.release();
254             }
255         }
256 
257         public void release()
258         {
259             // template method
260         }
261     }
262 
263     private class WriterWorker implements Work
264     {
265 
266         private OutputStream stream;
267         private OutMessage message;
268         private MessageContext context;
269         private Semaphore semaphore;
270 
271         public WriterWorker(OutputStream stream,
272                             OutMessage message,
273                             MessageContext context,
274                             Semaphore semaphore)
275         {
276             this.stream = stream;
277             this.message = message;
278             this.context = context;
279             this.semaphore = semaphore;
280         }
281 
282         public void run()
283         {
284             try
285             {
286                 final XMLStreamWriter writer = STAXUtils.createXMLStreamWriter(stream, message.getEncoding(),
287                     context);
288                 message.getSerializer().writeMessage(message, writer, context);
289 
290                 writer.close();
291                 stream.close();
292 
293             }
294             catch (Exception e)
295             {
296                 throw new XFireRuntimeException("Couldn't write stream.", e);
297             }
298             finally
299             {
300                 semaphore.release();
301             }
302         }
303 
304         public void release()
305         {
306             // template method
307         }
308     }
309 
310     /**
311      * Get the service that is mapped to the specified request.
312      */
313     protected String getService(UMOEventContext context)
314     {
315         String pathInfo = context.getEndpointURI().getPath();
316 
317         if (StringUtils.isEmpty(pathInfo))
318         {
319             return context.getEndpointURI().getHost();
320         }
321 
322         String serviceName;
323 
324         int i = pathInfo.lastIndexOf("/");
325 
326         if (i > -1)
327         {
328             serviceName = pathInfo.substring(i + 1);
329         }
330         else
331         {
332             serviceName = pathInfo;
333         }
334 
335         return serviceName;
336     }
337 
338     public Object onCall(UMOEventContext ctx) throws UMOException
339     {
340 
341         try
342         {
343             MessageContext context = new MessageContext();
344   
345             XFire xfire = (XFire)ctx.getComponentDescriptor().getProperties().get(
346                 XFireConnector.XFIRE_PROPERTY);
347 
348             context.setService(xfire.getServiceRegistry().getService(getService(ctx)));
349             context.setXFire(xfire);
350 
351             // Channel.BACKCHANNEL_URI
352             ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
353 
354             // Return the result to us, not to the sender.
355             context.setProperty(Channel.BACKCHANNEL_URI, resultStream);
356 
357             XMLStreamReader reader;
358 
359             // TODO isStreaming()?
360             Object payload = ctx.getMessage().getPayload();
361             if (payload instanceof InputStream)
362             {
363                 reader = STAXUtils.createXMLStreamReader((InputStream)payload, ctx.getEncoding(), context);
364             }
365             else if (payload instanceof Reader)
366             {
367                 reader = STAXUtils.createXMLStreamReader((Reader)payload, context);
368             }
369             else
370             {
371                 String text = ctx.getTransformedMessageAsString(ctx.getEncoding());
372                 reader = STAXUtils.createXMLStreamReader(new StringReader(text), context);
373             }
374 
375             InMessage in = new InMessage(reader, getUri());
376 
377             String soapAction = getSoapAction(ctx.getMessage());
378             in.setProperty(SoapConstants.SOAP_ACTION, soapAction);
379 
380             receive(context, in);
381 
382             Object result = null;
383 
384             try
385             {
386                 // We need to check if there is a fault message. If that's the case,
387                 // we need to send back the fault to the client.
388                 // TODO: see MULE-1113 for background about this workaround; I'm not
389                 // even sure the fault reading is done correctly? (XFire API is a bit
390                 // confusing)
391                 AbstractMessage fault = context.getExchange().getFaultMessage();
392                 if (fault != null && fault.getBody() != null)
393                 {
394                     result = resultStream.toString(fault.getEncoding());
395                     ExceptionPayload exceptionPayload = new ExceptionPayload(new Exception(result.toString()));
396                     ctx.getMessage().setExceptionPayload(exceptionPayload);
397                 }
398                 else if (context.getExchange().hasOutMessage())
399                 {
400                     result = resultStream.toString(context.getExchange().getOutMessage().getEncoding());
401                 }
402             }
403             catch (UnsupportedEncodingException e1)
404             {
405                 throw new MuleException(e1);
406             }
407 
408             return result;
409 
410         }
411         catch (UMOException e)
412         {
413             logger.warn("Could not dispatch message to XFire!", e);
414             throw e;
415         }
416     }
417 
418     private String getSoapAction(UMOMessage message) {
419         String action = (String) message.getProperty(SoapConstants.SOAP_ACTION);
420         
421         if (action != null && action.startsWith("\"") && action.endsWith("\"") && action.length() >= 2)
422         {
423             action = action.substring(1, action.length() - 1);
424         }
425         
426         return action;
427     }
428 
429 }