View Javadoc

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