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