View Javadoc

1   /*
2    * $Id: CxfInboundMessageProcessor.java 19191 2010-08-25 21:05:23Z tcarlson $
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.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      public void initialise() throws InitialisationException
86      {
87          if (bus == null)
88          {
89              throw new InitialisationException(
90                  MessageFactory.createStaticMessage("No CXF bus instance, this component has not been initialized properly."),
91                  this);
92          }
93  
94          if (server == null)
95          {
96              throw new InitialisationException(
97                  MessageFactory.createStaticMessage("No CXF Server instance, this component has not been initialized properly."),
98                  this);
99          }
100     }
101 
102     public void stop() throws MuleException
103     {
104         if (server != null)
105         {
106             server.stop();
107         }
108     }
109 
110     public void start() throws MuleException
111     {
112         // Start the CXF Server
113         server.start();
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 = getWsdlUri(event, req);
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().setProperty(HttpConstants.HEADER_CONTENT_TYPE, ct);
208         return event;
209     }
210 
211     private String getWsdlUri(MuleEvent event, String reqPath)
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         return epUri.getScheme() + "://" + host + ctx;
217     }
218 
219     protected MuleEvent sendToDestination(MuleEvent event) throws MuleException, IOException
220     {
221         try
222         {
223             final MessageImpl m = new MessageImpl();
224             final MuleMessage muleReqMsg = event.getMessage();
225             String method = muleReqMsg.getInboundProperty(HttpConnector.HTTP_METHOD_PROPERTY);
226 
227             String ct = muleReqMsg.getInboundProperty(HttpConstants.HEADER_CONTENT_TYPE);
228             if (ct != null)
229             {
230                 m.put(Message.CONTENT_TYPE, ct);
231             }
232 
233             String path = muleReqMsg.getInboundProperty(HttpConnector.HTTP_REQUEST_PATH_PROPERTY);
234             if (path == null)
235             {
236                 path = "";
237             }
238 
239             if (method != null)
240             {
241                 m.put(Message.HTTP_REQUEST_METHOD, method);
242                 m.put(Message.PATH_INFO, path);
243                 Object basePath = muleReqMsg.getInboundProperty(HttpConnector.HTTP_CONTEXT_PATH_PROPERTY);
244                 m.put(Message.BASE_PATH, basePath);
245 
246                 method = method.toUpperCase();
247             }
248 
249             if (!"GET".equals(method))
250             {
251                 Object payload = event.getMessage().getPayload();
252 
253                 setPayload(event, m, payload);
254             }
255 
256             // TODO: Not sure if this is 100% correct - DBD
257             String soapAction = getSoapAction(event.getMessage());
258             m.put(org.mule.module.cxf.SoapConstants.SOAP_ACTION_PROPERTY_CAPS, soapAction);
259 
260             org.apache.cxf.transport.Destination d = server.getDestination();
261 
262             // Set up a listener for the response
263             m.put(LocalConduit.DIRECT_DISPATCH, Boolean.TRUE);
264             m.put(MuleProperties.MULE_EVENT_PROPERTY, RequestContext.getEvent());
265             m.setDestination(d);
266 
267             ExchangeImpl exchange = new ExchangeImpl();
268             exchange.setInMessage(m);
269             m.put(CxfConstants.MULE_EVENT, event);
270 
271             // if there is a fault, then we need an event in here because we won't
272             // have a responseEvent from the MuleInvoker
273             exchange.put(CxfConstants.MULE_EVENT, event);
274 
275             // invoke the actual web service up until right before we serialize the
276             // response
277             d.getMessageObserver().onMessage(m);
278 
279             // get the response event
280             MuleEvent responseEvent = (MuleEvent) exchange.get(CxfConstants.MULE_EVENT);
281 
282             // If there isn't one, there was probably a fault, so use the original
283             // event
284             if (responseEvent == null || !event.getEndpoint().getExchangePattern().hasResponse())
285             {
286                 return null;
287             }
288             
289             MuleMessage muleResMsg = responseEvent.getMessage();
290             muleResMsg.setPayload(getRessponseOutputHandler(m));
291 
292             // Handle a fault if there is one.
293             Message faultMsg = m.getExchange().getOutFaultMessage();
294             if (faultMsg != null)
295             {
296                 Exception ex = faultMsg.getContent(Exception.class);
297                 if (ex != null)
298                 {
299                     ExceptionPayload exceptionPayload = new DefaultExceptionPayload(new Exception(""));
300                     event.getMessage().setExceptionPayload(exceptionPayload);
301                     muleResMsg.setOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY, 500);
302                 }
303             }
304 
305             return responseEvent;
306         }
307         catch (MuleException e)
308         {
309             logger.warn("Could not dispatch message to CXF!", e);
310             throw e;
311         }
312     }
313 
314     @Override
315     public MuleEvent processNext(MuleEvent event) throws MuleException
316     {
317         return next.process(event);
318     }
319 
320     protected OutputHandler getRessponseOutputHandler(final MessageImpl m)
321     {
322         OutputHandler outputHandler = new OutputHandler()
323         {
324             public void write(MuleEvent event, OutputStream out) throws IOException
325             {
326                 Message outFaultMessage = m.getExchange().getOutFaultMessage();
327                 Message outMessage = m.getExchange().getOutMessage();
328 
329                 Message contentMsg = null;
330                 if (outFaultMessage != null && outFaultMessage.getContent(OutputStream.class) != null)
331                 {
332                     contentMsg = outFaultMessage;
333                 }
334                 else if (outMessage != null)
335                 {
336                     contentMsg = outMessage;
337                 }
338 
339                 if (contentMsg == null)
340                 {
341                     return;
342                 }
343 
344                 DelegatingOutputStream delegate = contentMsg.getContent(DelegatingOutputStream.class);
345                 out.write(((ByteArrayOutputStream) delegate.getOutputStream()).toByteArray());
346                 delegate.setOutputStream(out);
347 
348                 out.flush();
349 
350                 contentMsg.getInterceptorChain().resume();
351             }
352 
353         };
354         return outputHandler;
355     }
356 
357     private void setPayload(MuleEvent ctx, final MessageImpl m, Object payload) throws TransformerException
358     {
359         if (payload instanceof InputStream)
360         {
361             m.put(Message.ENCODING, ctx.getEncoding());
362             m.setContent(InputStream.class, payload);
363         }
364         else if (payload instanceof Reader)
365         {
366             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Reader) payload));
367         }
368         else if (payload instanceof byte[])
369         {
370             m.setContent(InputStream.class, new ByteArrayInputStream((byte[]) payload));
371         }
372         else if (payload instanceof StaxSource)
373         {
374             m.setContent(XMLStreamReader.class, ((StaxSource) payload).getXMLStreamReader());
375         }
376         else if (payload instanceof Source)
377         {
378             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Source) payload));
379         }
380         else if (payload instanceof XMLStreamReader)
381         {
382             m.setContent(XMLStreamReader.class, payload);
383         }
384         else if (payload instanceof Document)
385         {
386             DOMSource source = new DOMSource((Node) payload);
387             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader(source));
388         }
389         else
390         {
391             InputStream is = (InputStream) ctx.transformMessage(DataTypeFactory.create(InputStream.class));
392             m.put(Message.ENCODING, ctx.getEncoding());
393             m.setContent(InputStream.class, is);
394         }
395     }
396 
397     /**
398      * Gets the stream representation of the current message.
399      * 
400      * @param context the event context
401      * @return The inputstream for the current message
402      * @throws MuleException
403      */
404     protected InputStream getMessageStream(MuleEvent context) throws MuleException
405     {
406         InputStream is;
407         Object eventMsgPayload = context.getMessage().getPayload();
408 
409         if (eventMsgPayload instanceof InputStream)
410         {
411             is = (InputStream) eventMsgPayload;
412         }
413         else
414         {
415             is = (InputStream) context.transformMessage(DataTypeFactory.create(InputStream.class));
416         }
417         return is;
418     }
419 
420     protected String getSoapAction(MuleMessage message)
421     {
422         String action = message.getInboundProperty(SoapConstants.SOAP_ACTION_PROPERTY);
423 
424         if (action != null && action.startsWith("\"") && action.endsWith("\"") && action.length() >= 2)
425         {
426             action = action.substring(1, action.length() - 1);
427         }
428 
429         return action;
430     }
431 
432     public Bus getBus()
433     {
434         return bus;
435     }
436 
437     public void setBus(Bus bus)
438     {
439         this.bus = bus;
440     }
441 
442     public Server getServer()
443     {
444         return server;
445     }
446 
447     public void setServer(Server server)
448     {
449         this.server = server;
450     }
451 
452     public void setProxy(boolean proxy)
453     {
454         this.proxy = proxy;
455     }
456 
457     public boolean isProxy()
458     {
459         return proxy;
460     }
461 }