View Javadoc

1   /*
2    * $Id: CxfServiceComponent.java 12283 2008-07-10 18:16:53Z dandiep $
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.transport.cxf;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.RequestContext;
15  import org.mule.api.ExceptionPayload;
16  import org.mule.api.MuleEvent;
17  import org.mule.api.MuleEventContext;
18  import org.mule.api.MuleException;
19  import org.mule.api.MuleMessage;
20  import org.mule.api.config.ConfigurationException;
21  import org.mule.api.config.MuleProperties;
22  import org.mule.api.endpoint.EndpointNotFoundException;
23  import org.mule.api.endpoint.EndpointURI;
24  import org.mule.api.lifecycle.Callable;
25  import org.mule.api.lifecycle.InitialisationException;
26  import org.mule.api.lifecycle.Lifecycle;
27  import org.mule.api.transformer.TransformerException;
28  import org.mule.api.transport.OutputHandler;
29  import org.mule.config.i18n.MessageFactory;
30  import org.mule.message.DefaultExceptionPayload;
31  import org.mule.module.xml.stax.StaxSource;
32  import org.mule.transport.cxf.support.DelegatingOutputStream;
33  import org.mule.transport.http.HttpConnector;
34  import org.mule.transport.http.HttpConstants;
35  import org.mule.transport.soap.SoapConstants;
36  import org.mule.util.StringUtils;
37  
38  import java.io.ByteArrayInputStream;
39  import java.io.ByteArrayOutputStream;
40  import java.io.IOException;
41  import java.io.InputStream;
42  import java.io.OutputStream;
43  import java.io.Reader;
44  
45  import javax.xml.stream.XMLStreamReader;
46  import javax.xml.transform.Source;
47  import javax.xml.transform.dom.DOMSource;
48  
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  import org.apache.cxf.Bus;
52  import org.apache.cxf.endpoint.Server;
53  import org.apache.cxf.io.CachedOutputStream;
54  import org.apache.cxf.message.ExchangeImpl;
55  import org.apache.cxf.message.Message;
56  import org.apache.cxf.message.MessageImpl;
57  import org.apache.cxf.service.model.EndpointInfo;
58  import org.apache.cxf.staxutils.StaxUtils;
59  import org.apache.cxf.transport.MessageObserver;
60  import org.apache.cxf.transport.local.LocalConduit;
61  import org.apache.cxf.transports.http.QueryHandler;
62  import org.apache.cxf.transports.http.QueryHandlerRegistry;
63  import org.w3c.dom.Document;
64  import org.w3c.dom.Node;
65  import org.xmlsoap.schemas.wsdl.http.AddressType;
66  
67  /**
68   * The CXF receives messages from Mule, converts them into CXF messages and dispatches
69   * them into the receiving CXF destination.
70   */
71  public class CxfServiceComponent implements Callable, 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      private CxfMessageReceiver receiver;
84  
85      private final CxfConnector connector;
86  
87      public CxfServiceComponent(CxfConnector connector,
88                                 CxfMessageReceiver receiver) throws ConfigurationException
89      {
90          super();
91          this.connector = connector;
92          this.receiver = receiver;
93          this.bus = receiver.connector.getCxfBus();
94      }
95  
96  
97      public Object onCall(MuleEventContext eventContext) throws Exception
98      {
99          if (logger.isDebugEnabled())
100         {
101             logger.debug(eventContext);
102         }
103 
104         // if http request
105         String request = eventContext.getMessage().getStringProperty(HttpConnector.HTTP_REQUEST_PROPERTY,
106             StringUtils.EMPTY);
107         String uri = eventContext.getEndpointURI().toString();
108 
109         if (request.indexOf('?') > -1 || uri.indexOf('?') > -1)
110         {
111             return generateWSDLOrXSD(eventContext, request, uri);
112         }
113         else
114         {
115             return sendToDestination(eventContext, uri);
116         }
117     }
118 
119     protected Object generateWSDLOrXSD(MuleEventContext eventContext, String req, String uri)
120         throws EndpointNotFoundException, IOException
121     {
122         
123         // TODO: Is there a way to make this not so ugly?
124         String ctxUri;
125         String uriBase = (String) eventContext.getMessage().getProperty(MuleProperties.MULE_ENDPOINT_PROPERTY);
126         
127         if (uriBase == null) {
128             EndpointURI epUri = eventContext.getEndpointURI();
129             String host = (String) eventContext.getMessage().getProperty("Host", epUri.getHost());
130             
131             uriBase = epUri.getScheme() + "://" + host + epUri.getPath();
132         }
133         
134         // This is the case of the HTTP message receiver. The servlet one sends different info
135         if (req != null && req.length() > 0) {
136             int qIdx = uriBase.indexOf('?');
137             if (qIdx > -1) {
138                 uriBase = uriBase.substring(0, qIdx);
139             }
140             
141             qIdx = req.indexOf('?');
142             if (qIdx > -1) {
143                 req = req.substring(qIdx);
144             }
145             
146             qIdx = req.indexOf('&');
147             if (qIdx > -1) {
148                 req = req.substring(0, qIdx);
149             }
150             
151             uri = uriBase + req;
152         }
153        
154         ctxUri = eventContext.getEndpointURI().getPath();
155         
156         EndpointInfo ei = receiver.getServer().getEndpoint().getEndpointInfo();
157 
158         if (uriBase != null) {
159             ei.setAddress(uriBase);
160             
161             if (ei.getExtensor(AddressType.class) != null) {
162                 ei.getExtensor(AddressType.class).setLocation(uriBase);
163             }
164         }
165 
166         ByteArrayOutputStream out = new ByteArrayOutputStream();
167         String ct = null;
168 
169         for (QueryHandler qh : bus.getExtension(QueryHandlerRegistry.class).getHandlers())
170         {
171             if (qh.isRecognizedQuery(uri, ctxUri, ei))
172             {
173                 ct = qh.getResponseContentType(uri, ctxUri);
174                 qh.writeResponse(uri, ctxUri, ei, out);
175                 out.flush();
176             }
177         }
178 
179         String msg;
180         if (ct == null)
181         {
182             ct = "text/plain";
183             msg = "No query handler found for URL.";
184         }
185         else
186         {
187             msg = out.toString();
188         }
189 
190         MuleMessage result = new DefaultMuleMessage(msg);
191         result.setProperty(HttpConstants.HEADER_CONTENT_TYPE, ct);
192 
193         return result;
194     }
195 
196     protected Object sendToDestination(MuleEventContext ctx, String uri) throws MuleException, IOException
197     {
198         try
199         {
200             final MessageImpl m = new MessageImpl();
201             final MuleMessage muleReqMsg = ctx.getMessage();
202             String method = (String) muleReqMsg.getProperty(HttpConnector.HTTP_METHOD_PROPERTY);
203             
204             String ct = (String) muleReqMsg.getProperty(HttpConstants.HEADER_CONTENT_TYPE);
205             if (ct != null) {
206                 m.put(Message.CONTENT_TYPE, ct);
207             }
208             
209             String path = (String) muleReqMsg.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
210             if (path == null) 
211             {
212                 path = "";
213             }
214             
215             if (method != null) 
216             {
217                 m.put(Message.HTTP_REQUEST_METHOD, method);
218                 m.put(Message.PATH_INFO, path);
219                 m.put(Message.BASE_PATH, ctx.getEndpointURI().getPath());
220                 
221                 method = method.toUpperCase();
222             }
223             
224             if (!"GET".equals(method)) 
225             {
226                 Object payload = ctx.transformMessage();
227 
228                 setPayload(ctx, m, payload);
229             }
230             
231             // TODO: Not sure if this is 100% correct - DBD
232             String soapAction = getSoapAction(ctx.getMessage());
233             m.put(org.mule.transport.soap.SoapConstants.SOAP_ACTION_PROPERTY_CAPS, soapAction);
234 
235             EndpointURI epUri = ctx.getEndpointURI();
236             Server server = connector.getServer(epUri.toString());
237             if (server == null)
238             {
239                 // TODO is this the right Mule exception?
240                 throw new EndpointNotFoundException(uri);
241             }
242 
243             org.apache.cxf.transport.Destination d = server.getDestination();
244             // Set up a listener for the response
245             m.put(LocalConduit.DIRECT_DISPATCH, Boolean.TRUE);
246             m.put(MuleProperties.MULE_EVENT_PROPERTY, RequestContext.getEvent());
247             m.setDestination(d);
248             
249             OutputHandler outputHandler = new OutputHandler() 
250             {
251                 public void write(MuleEvent event, OutputStream out) throws IOException
252                 {
253                     Message outFaultMessage = m.getExchange().getOutFaultMessage();
254                     Message outMessage = m.getExchange().getOutMessage();
255                     
256                     Message contentMsg = null;
257                     if (outFaultMessage != null) 
258                     {
259                         contentMsg = outFaultMessage;
260                     } 
261                     else if (outMessage != null) 
262                     {
263                         contentMsg = outMessage;
264                     }
265                     
266                     if (contentMsg == null)
267                     {
268                         return;
269                     }
270                     
271                     DelegatingOutputStream delegate = (DelegatingOutputStream) contentMsg.getContent(OutputStream.class);
272                     out.write(((ByteArrayOutputStream) delegate.getOutputStream()).toByteArray());
273                     delegate.setOutputStream(out);
274                     
275                     contentMsg.getInterceptorChain().resume();
276                     
277                     out.flush();
278                 }
279                 
280             };
281             DefaultMuleMessage muleResMsg = new DefaultMuleMessage(outputHandler);
282             
283             ExchangeImpl exchange = new ExchangeImpl();
284             exchange.setInMessage(m);
285             m.put(CxfConstants.MULE_MESSAGE, muleReqMsg);
286             
287             exchange.put(CxfConstants.MULE_MESSAGE, muleResMsg);
288             
289             // invoke the actual web service up until right before we serialize the response
290             d.getMessageObserver().onMessage(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                     ctx.getMessage().setExceptionPayload(exceptionPayload);
301                 }
302             }
303             
304             return muleResMsg;
305         }
306         catch (MuleException e)
307         {
308             logger.warn("Could not dispatch message to XFire!", e);
309             throw e;
310         }
311     }
312 
313 
314     private void setPayload(MuleEventContext ctx, final MessageImpl m, Object payload)
315         throws TransformerException
316     {
317         if (payload instanceof InputStream)
318         {
319             m.put(Message.ENCODING, ctx.getEncoding());
320             m.setContent(InputStream.class, payload);
321         }
322         else if (payload instanceof Reader)
323         {
324             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Reader) payload));
325         }
326         else if (payload instanceof byte[])
327         {
328             m.setContent(InputStream.class, new ByteArrayInputStream((byte[]) payload));
329         }
330         else if (payload instanceof StaxSource)
331         {
332             m.setContent(XMLStreamReader.class, ((StaxSource) payload).getXMLStreamReader());
333         }
334         else if (payload instanceof Source)
335         {
336             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader((Source) payload));
337         }
338         else if (payload instanceof XMLStreamReader)
339         {
340             m.setContent(XMLStreamReader.class, (XMLStreamReader) payload);
341         }
342         else if (payload instanceof Document)
343         {
344             DOMSource source = new DOMSource((Node) payload);
345             m.setContent(XMLStreamReader.class, StaxUtils.createXMLStreamReader(source));
346         }
347         else
348         {
349             InputStream is = (InputStream) ctx.transformMessage(InputStream.class);
350             m.put(Message.ENCODING, ctx.getEncoding());
351             m.setContent(InputStream.class, is);
352         }
353     }
354 
355     /**
356      * Gets the stream representation of the current message. If the message is set
357      * for streaming the input stream on the UMOStreamMEssageAdapter will be used,
358      * otherwise a byteArrayInputStream will be used to hold the byte[]
359      * representation of the current message.
360      * 
361      * @param context the event context
362      * @return The inputstream for the current message
363      * @throws MuleException
364      */
365 
366     protected InputStream getMessageStream(MuleEventContext context) throws MuleException
367     {
368         InputStream is;
369         Object eventMsgPayload = context.transformMessage();
370 
371         if (eventMsgPayload instanceof InputStream)
372         {
373             is = (InputStream) eventMsgPayload;
374         }
375         else
376         {
377             is = (InputStream) context.transformMessage(InputStream.class);
378         }
379         return is;
380     }
381 
382     protected String getSoapAction(MuleMessage message)
383     {
384         String action = (String) message.getProperty(SoapConstants.SOAP_ACTION_PROPERTY);
385 
386         if (action != null && action.startsWith("\"") && action.endsWith("\"") && action.length() >= 2)
387         {
388             action = action.substring(1, action.length() - 1);
389         }
390 
391         return action;
392     }
393 
394     public Bus getBus()
395     {
396         return bus;
397     }
398 
399     public void setBus(Bus bus)
400     {
401         this.bus = bus;
402     }
403 
404     class ResponseListener implements MessageObserver
405     {
406         private Message message;
407 
408         public CachedOutputStream getCachedStream()
409         {
410             return message.getContent(CachedOutputStream.class);
411         }
412 
413         public Message getMessage()
414         {
415             return message;
416         }
417 
418         public synchronized void onMessage(Message message)
419         {
420             this.message = message;
421         }
422     }
423 
424     public void initialise() throws InitialisationException
425     {
426         if (bus == null)
427         {
428             throw new InitialisationException(MessageFactory.createStaticMessage("No Cxf bus instance, this component has not been initialized properly."), this);
429         }
430     }
431 
432     public void start() throws MuleException
433     {
434         // nothing to do
435     }
436     
437     public void stop() throws MuleException
438     {
439         // nothing to do
440     }
441 
442     public void dispose()
443     {
444         // template method
445     }
446 }