View Javadoc

1   /*
2    * $Id: CxfInboundMessageProcessor.java 22510 2011-07-21 21:19:58Z evangelinamrm $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.module.cxf;
12  
13  import org.mule.api.DefaultMuleException;
14  import org.mule.api.ExceptionPayload;
15  import org.mule.api.MuleEvent;
16  import org.mule.api.MuleException;
17  import org.mule.api.MuleMessage;
18  import org.mule.api.endpoint.EndpointNotFoundException;
19  import org.mule.api.lifecycle.InitialisationException;
20  import org.mule.api.lifecycle.Lifecycle;
21  import org.mule.api.transformer.TransformerException;
22  import org.mule.api.transport.OutputHandler;
23  import org.mule.config.i18n.MessageFactory;
24  import org.mule.message.DefaultExceptionPayload;
25  import org.mule.module.cxf.support.DelegatingOutputStream;
26  import org.mule.module.xml.stax.StaxSource;
27  import org.mule.processor.AbstractInterceptingMessageProcessor;
28  import org.mule.transformer.types.DataTypeFactory;
29  import org.mule.transport.http.HttpConnector;
30  import org.mule.transport.http.HttpConstants;
31  import org.mule.util.StringUtils;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.ByteArrayOutputStream;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.io.OutputStream;
38  import java.io.Reader;
39  import java.net.URI;
40  
41  import javax.xml.stream.XMLStreamReader;
42  import javax.xml.transform.Source;
43  import javax.xml.transform.dom.DOMSource;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  import org.apache.cxf.Bus;
48  import org.apache.cxf.endpoint.Server;
49  import org.apache.cxf.message.ExchangeImpl;
50  import org.apache.cxf.message.Message;
51  import org.apache.cxf.message.MessageImpl;
52  import org.apache.cxf.service.model.EndpointInfo;
53  import org.apache.cxf.staxutils.StaxUtils;
54  import org.apache.cxf.transport.DestinationFactory;
55  import org.apache.cxf.transport.DestinationFactoryManager;
56  import org.apache.cxf.transport.local.LocalConduit;
57  import org.apache.cxf.transports.http.QueryHandler;
58  import org.apache.cxf.transports.http.QueryHandlerRegistry;
59  import org.apache.cxf.wsdl.http.AddressType;
60  import org.w3c.dom.Document;
61  import org.w3c.dom.Node;
62  
63  /**
64   * The CxfInboundMessageProcessor performs inbound CXF processing, sending an event
65   * through the CXF service, then on to the next MessageProcessor. This processor gets
66   * built by a MessageProcessorBuilder which is responsible for configuring it and the
67   * {@link Server} that it dispatches to.
68   */
69  public class CxfInboundMessageProcessor extends AbstractInterceptingMessageProcessor implements Lifecycle
70  {
71      /**
72       * logger used by this class
73       */
74      protected transient Log logger = LogFactory.getLog(getClass());
75  
76      protected Bus bus;
77  
78      // manager to the component
79      protected String transportClass;
80  
81      protected Server server;
82  
83      private boolean proxy;
84  
85      private boolean onFaultInvokeStrategy = false;
86      
87      @Override
88      public void initialise() throws InitialisationException
89      {
90          if (bus == null)
91          {
92              throw new InitialisationException(
93                  MessageFactory.createStaticMessage("No CXF bus instance, this component has not been initialized properly."),
94                  this);
95          }
96      }
97  
98      @Override
99      public void stop() throws MuleException
100     {
101         if (server != null)
102         {
103             server.stop();
104         }
105     }
106 
107     @Override
108     public void start() throws MuleException
109     {
110         // Start the CXF Server
111         if (server != null)
112         {
113             server.start();
114         }
115     }
116 
117     @Override
118     public void dispose()
119     {
120         // nothing to do
121     }
122 
123     @Override
124     public MuleEvent process(MuleEvent event) throws MuleException
125     {
126         // if http request
127         String requestPath = parseHttpRequestProperty(event.getMessage().getInboundProperty(
128             HttpConnector.HTTP_REQUEST_PROPERTY, StringUtils.EMPTY));
129         try
130         {
131             if (requestPath.indexOf('?') > -1)
132             {
133                 return generateWSDLOrXSD(event, requestPath);
134             }
135             else
136             {
137                 return sendToDestination(event);
138             }
139         }
140         catch (IOException e)
141         {
142             throw new DefaultMuleException(e);
143         }
144     }
145 
146     private String parseHttpRequestProperty(String request)
147     {
148         String uriBase = "";
149 
150         if (!(request.contains("?wsdl")) && (!(request.contains("?xsd"))))
151         {
152             int qIdx = request.indexOf('?');
153             if (qIdx > -1)
154             {
155                 uriBase = request.substring(0, qIdx);
156             }
157         }
158         else
159         {
160             uriBase = request;
161         }
162 
163         return uriBase;
164     }
165 
166     protected MuleEvent generateWSDLOrXSD(MuleEvent event, String req)
167         throws EndpointNotFoundException, IOException
168     {
169         // TODO: Is there a way to make this not so ugly?
170         String ctxUri = event.getMessage().getInboundProperty(HttpConnector.HTTP_CONTEXT_PATH_PROPERTY);
171         String wsdlUri = getUri(event);
172         String serviceUri = wsdlUri.substring(0, wsdlUri.indexOf('?'));
173 
174         EndpointInfo ei = getServer().getEndpoint().getEndpointInfo();
175 
176         if (serviceUri != null)
177         {
178             ei.setAddress(serviceUri);
179 
180             if (ei.getExtensor(AddressType.class) != null)
181             {
182                 ei.getExtensor(AddressType.class).setLocation(serviceUri);
183             }
184         }
185 
186         ByteArrayOutputStream out = new ByteArrayOutputStream();
187         String ct = null;
188 
189         for (QueryHandler qh : bus.getExtension(QueryHandlerRegistry.class).getHandlers())
190         {
191             if (qh.isRecognizedQuery(wsdlUri, ctxUri, ei))
192             {
193                 ct = qh.getResponseContentType(wsdlUri, ctxUri);
194                 qh.writeResponse(wsdlUri, ctxUri, ei, out);
195                 out.flush();
196             }
197         }
198 
199         String msg;
200         if (ct == null)
201         {
202             ct = "text/plain";
203             msg = "No query handler found for URL.";
204         }
205         else
206         {
207             msg = out.toString();
208         }
209 
210         event.getMessage().setPayload(msg);
211         event.getMessage().setOutboundProperty(HttpConstants.HEADER_CONTENT_TYPE, ct);
212         return event;
213     }
214 
215     private String getUri(MuleEvent event)
216     {
217         URI epUri = event.getMessageSourceURI();
218         String host = event.getMessage().getInboundProperty("Host", epUri.getHost());
219         String ctx = event.getMessage().getInboundProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
220 
221         return epUri.getScheme() + "://" + host + ctx;
222     }
223 
224     protected MuleEvent sendToDestination(MuleEvent event) throws MuleException, IOException
225     {
226         try
227         {
228             final MessageImpl m = new MessageImpl();
229             final MuleMessage muleReqMsg = event.getMessage();
230             String method = muleReqMsg.getInboundProperty(HttpConnector.HTTP_METHOD_PROPERTY);
231 
232             String ct = muleReqMsg.getInboundProperty(HttpConstants.HEADER_CONTENT_TYPE);
233             if (ct != null)
234             {
235                 m.put(Message.CONTENT_TYPE, ct);
236             }
237 
238             String path = muleReqMsg.getInboundProperty(HttpConnector.HTTP_REQUEST_PATH_PROPERTY);
239             if (path == null)
240             {
241                 path = "";
242             }
243 
244             if (method != null)
245             {
246                 m.put(Message.HTTP_REQUEST_METHOD, method);
247                 m.put(Message.PATH_INFO, path);
248                 Object basePath = muleReqMsg.getInboundProperty(HttpConnector.HTTP_CONTEXT_PATH_PROPERTY);
249                 m.put(Message.BASE_PATH, basePath);
250 
251                 method = method.toUpperCase();
252             }
253 
254             if (!"GET".equals(method))
255             {
256                 Object payload = event.getMessage().getPayload();
257 
258                 setPayload(event, m, payload);
259             }
260 
261             // TODO: Not sure if this is 100% correct - DBD
262             String soapAction = getSoapAction(event.getMessage());
263             m.put(org.mule.module.cxf.SoapConstants.SOAP_ACTION_PROPERTY_CAPS, soapAction);
264 
265             org.apache.cxf.transport.Destination d;
266             
267             if (server != null) 
268             {
269                 d = server.getDestination();
270             }
271             else
272             {
273                 String serviceUri = getUri(event);
274 
275                 DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class);
276                 DestinationFactory df = dfm.getDestinationFactoryForUri(serviceUri);
277                 
278                 EndpointInfo ei = new EndpointInfo();
279                 ei.setAddress(serviceUri);
280                 d = df.getDestination(ei);
281             }
282 
283             // Set up a listener for the response
284             m.put(LocalConduit.DIRECT_DISPATCH, Boolean.TRUE);
285             m.setDestination(d);
286 
287             ExchangeImpl exchange = new ExchangeImpl();
288             exchange.setInMessage(m);
289 
290             // if there is a fault, then we need an event in here because we won't
291             // have a responseEvent from the MuleInvoker
292             exchange.put(CxfConstants.MULE_EVENT, event);
293 
294             // invoke the actual web service up until right before we serialize the
295             // response
296             d.getMessageObserver().onMessage(m);
297             
298             // get the response event
299             MuleEvent responseEvent = (MuleEvent) exchange.get(CxfConstants.MULE_EVENT);
300 
301             // If there isn't one, there was probably a fault, so use the original
302             // event
303             if (responseEvent == null || !event.getExchangePattern().hasResponse())
304             {
305                 return null;
306             }
307             
308             MuleMessage muleResMsg = responseEvent.getMessage();
309             muleResMsg.setPayload(getRessponseOutputHandler(m));
310 
311             // Handle a fault if there is one.
312             Message faultMsg = m.getExchange().getOutFaultMessage();
313             if (faultMsg != null)
314             {
315                 Exception ex = faultMsg.getContent(Exception.class);
316                 if (ex != null)
317                 {
318                     ExceptionPayload exceptionPayload = new DefaultExceptionPayload(ex);
319                     event.getMessage().setExceptionPayload(exceptionPayload);
320                     muleResMsg.setOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY, 500);
321                 }
322             }
323 
324             //If SoapFault should trigger Mule ExceptionStrategy instead of handling it by CXF - intercept it here
325             if (isOnFaultInvokeStrategy())
326             {
327                 Object o = exchange.get(Exception.class);
328                 if (o != null && o instanceof Exception)
329                 {
330                     throw new DefaultMuleException((Exception)o);
331                 }
332             }
333 
334             return responseEvent;
335         }
336         catch (MuleException e)
337         {
338             logger.warn("Could not dispatch message to CXF!", e);
339             throw e;
340         }
341     }
342 
343     @Override
344     public MuleEvent processNext(MuleEvent event) throws MuleException
345     {
346         return super.processNext(event);
347     }
348 
349     protected OutputHandler getRessponseOutputHandler(final MessageImpl m)
350     {
351         OutputHandler outputHandler = new OutputHandler()
352         {
353             @Override
354             public void write(MuleEvent event, OutputStream out) throws IOException
355             {
356                 Message outFaultMessage = m.getExchange().getOutFaultMessage();
357                 Message outMessage = m.getExchange().getOutMessage();
358 
359                 Message contentMsg = null;
360                 if (outFaultMessage != null && outFaultMessage.getContent(OutputStream.class) != null)
361                 {
362                     contentMsg = outFaultMessage;
363                 }
364                 else if (outMessage != null)
365                 {
366                     contentMsg = outMessage;
367                 }
368 
369                 if (contentMsg == null)
370                 {
371                     return;
372                 }
373 
374                 DelegatingOutputStream delegate = contentMsg.getContent(DelegatingOutputStream.class);
375                 if (delegate.getOutputStream() instanceof ByteArrayOutputStream)
376                 {
377                     out.write(((ByteArrayOutputStream) delegate.getOutputStream()).toByteArray());
378                 }
379                 delegate.setOutputStream(out);
380 
381                 out.flush();
382 
383                 contentMsg.getInterceptorChain().resume();
384             }
385 
386         };
387         return outputHandler;
388     }
389 
390     private void setPayload(MuleEvent ctx, final MessageImpl m, Object payload) throws TransformerException
391     {
392         if (payload instanceof InputStream)
393         {
394             m.put(Message.ENCODING, ctx.getEncoding());
395             m.setContent(InputStream.class, payload);
396         }
397         else if (payload instanceof Reader)
398         {
399             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Reader) payload));
400         }
401         else if (payload instanceof byte[])
402         {
403             m.setContent(InputStream.class, new ByteArrayInputStream((byte[]) payload));
404         }
405         else if (payload instanceof StaxSource)
406         {
407             m.setContent(XMLStreamReader.class, ((StaxSource) payload).getXMLStreamReader());
408         }
409         else if (payload instanceof Source)
410         {
411             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Source) payload));
412         }
413         else if (payload instanceof XMLStreamReader)
414         {
415             m.setContent(XMLStreamReader.class, payload);
416         }
417         else if (payload instanceof Document)
418         {
419             DOMSource source = new DOMSource((Node) payload);
420             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader(source));
421         }
422         else
423         {
424             InputStream is = ctx.transformMessage(DataTypeFactory.create(InputStream.class));
425             m.put(Message.ENCODING, ctx.getEncoding());
426             m.setContent(InputStream.class, is);
427         }
428     }
429 
430     /**
431      * Gets the stream representation of the current message.
432      * 
433      * @param context the event context
434      * @return The inputstream for the current message
435      * @throws MuleException
436      */
437     protected InputStream getMessageStream(MuleEvent context) throws MuleException
438     {
439         InputStream is;
440         Object eventMsgPayload = context.getMessage().getPayload();
441 
442         if (eventMsgPayload instanceof InputStream)
443         {
444             is = (InputStream) eventMsgPayload;
445         }
446         else
447         {
448             is = context.transformMessage(DataTypeFactory.create(InputStream.class));
449         }
450         return is;
451     }
452 
453     protected String getSoapAction(MuleMessage message)
454     {
455         String action = message.getInboundProperty(SoapConstants.SOAP_ACTION_PROPERTY);
456 
457         if (action != null && action.startsWith("\"") && action.endsWith("\"") && action.length() >= 2)
458         {
459             action = action.substring(1, action.length() - 1);
460         }
461 
462         return action;
463     }
464 
465     public Bus getBus()
466     {
467         return bus;
468     }
469 
470     public void setBus(Bus bus)
471     {
472         this.bus = bus;
473     }
474 
475     public Server getServer()
476     {
477         return server;
478     }
479 
480     public void setServer(Server server)
481     {
482         this.server = server;
483     }
484 
485     public void setProxy(boolean proxy)
486     {
487         this.proxy = proxy;
488     }
489 
490     public boolean isProxy()
491     {
492         return proxy;
493     }
494     
495     public void setOnFaultInvokeStrategy(boolean onFaultInvokeStrategy) 
496     {
497         this.onFaultInvokeStrategy = onFaultInvokeStrategy;
498     }
499     
500     public boolean isOnFaultInvokeStrategy() 
501     {
502         return onFaultInvokeStrategy;
503     }
504 }