View Javadoc

1   /*
2    * $Id: MuleUniversalConduit.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.transport;
12  
13  import static org.apache.cxf.message.Message.DECOUPLED_CHANNEL_MESSAGE;
14  
15  import org.mule.DefaultMuleEvent;
16  import org.mule.DefaultMuleMessage;
17  import org.mule.DefaultMuleSession;
18  import org.mule.RegistryContext;
19  import org.mule.RequestContext;
20  import org.mule.api.MuleEvent;
21  import org.mule.api.MuleEventContext;
22  import org.mule.api.MuleException;
23  import org.mule.api.MuleMessage;
24  import org.mule.api.MuleSession;
25  import org.mule.api.endpoint.OutboundEndpoint;
26  import org.mule.api.transport.MessageAdapter;
27  import org.mule.api.transport.OutputHandler;
28  import org.mule.transport.DefaultMessageAdapter;
29  import org.mule.transport.cxf.CxfConnector;
30  import org.mule.transport.cxf.CxfConstants;
31  import org.mule.transport.cxf.support.DelegatingOutputStream;
32  import org.mule.transport.cxf.support.MuleProtocolHeadersOutInterceptor;
33  import org.mule.transport.http.HttpConstants;
34  
35  import java.io.ByteArrayOutputStream;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.io.OutputStream;
39  import java.net.HttpURLConnection;
40  import java.net.MalformedURLException;
41  import java.util.logging.Logger;
42  
43  import org.apache.cxf.interceptor.Fault;
44  import org.apache.cxf.message.Exchange;
45  import org.apache.cxf.message.ExchangeImpl;
46  import org.apache.cxf.message.Message;
47  import org.apache.cxf.message.MessageImpl;
48  import org.apache.cxf.phase.AbstractPhaseInterceptor;
49  import org.apache.cxf.phase.Phase;
50  import org.apache.cxf.service.model.EndpointInfo;
51  import org.apache.cxf.transport.AbstractConduit;
52  import org.apache.cxf.transport.Destination;
53  import org.apache.cxf.transport.MessageObserver;
54  import org.apache.cxf.ws.addressing.AttributedURIType;
55  import org.apache.cxf.ws.addressing.EndpointReferenceType;
56  import org.apache.cxf.wsdl.EndpointReferenceUtils;
57  
58  /**
59   * A Conduit is primarily responsible for sending messages from CXF to somewhere
60   * else. This conduit takes messages which are being written and sends them to the
61   * Mule bus.
62   */
63  public class MuleUniversalConduit extends AbstractConduit
64  {
65  
66      private static final Logger LOGGER = Logger.getLogger(MuleUniversalConduit.class.getName());
67  
68      private EndpointInfo endpoint;
69  
70      private CxfConnector connector;
71  
72      private Destination decoupledDestination;
73  
74      private String decoupledEndpoint;
75  
76      private MuleUniversalTransport transport;
77  
78      private int decoupledDestinationRefCount;
79  
80      /**
81       * @param ei The Endpoint being invoked by this destination.
82       * @param t The EPR associated with this Conduit - i.e. the reply destination.
83       */
84      public MuleUniversalConduit(MuleUniversalTransport transport,
85                                  CxfConnector connector,
86                                  EndpointInfo ei,
87                                  EndpointReferenceType t)
88      {
89          super(getTargetReference(ei, t));
90          this.transport = transport;
91          this.endpoint = ei;
92          this.connector = connector;
93      }
94  
95      @Override
96      protected Logger getLogger()
97      {
98          return LOGGER;
99      }
100 
101     public synchronized Destination getBackChannel()
102     {
103         if (decoupledDestination == null && decoupledEndpoint != null)
104         {
105             setUpDecoupledDestination();
106         }
107         return decoupledDestination;
108     }
109 
110     private void setUpDecoupledDestination()
111     {
112         EndpointInfo ei = new EndpointInfo();
113         ei.setAddress(decoupledEndpoint);
114         try
115         {
116             decoupledDestination = transport.getDestination(ei);
117             decoupledDestination.setMessageObserver(new InterposedMessageObserver());
118             duplicateDecoupledDestination();
119         }
120         catch (IOException e)
121         {
122             throw new RuntimeException(e);
123         }
124     }
125 
126     /**
127      * Prepare the message for writing.
128      */
129     public void prepare(final Message message) throws IOException
130     {
131         // save in a separate place in case we need to resend the request
132         final ByteArrayOutputStream cache = new ByteArrayOutputStream();
133         final DelegatingOutputStream delegating = new DelegatingOutputStream(cache);
134         message.setContent(OutputStream.class, delegating);
135         
136         AbstractPhaseInterceptor<Message> i = new AbstractPhaseInterceptor<Message>(Phase.PRE_STREAM)
137         {
138             public void handleMessage(Message m) throws Fault
139             {
140                 try
141                 {
142                     dispatchMuleMessage(m);
143                 }
144                 catch (IOException e)
145                 {
146                     throw new Fault(e);
147                 }
148             }
149         };
150         i.getAfter().add(MuleProtocolHeadersOutInterceptor.class.getName());
151         message.getInterceptorChain().add(i);
152         
153         OutputHandler handler = new OutputHandler()
154         {
155             public void write(MuleEvent event, OutputStream out) throws IOException
156             {
157                 out.write(cache.toByteArray());
158                 
159                 delegating.setOutputStream(out);
160                 
161                 // resume writing!
162                 message.getInterceptorChain().doIntercept(message);
163             }
164         };
165 
166         // We can create a generic StreamMessageAdapter here as the underlying
167         // transport will create one specific to the transport
168         DefaultMessageAdapter req = new DefaultMessageAdapter(handler);
169         message.getExchange().put(CxfConstants.MULE_MESSAGE, req);
170     }
171     
172     protected void dispatchMuleMessage(Message m) throws IOException {
173         String uri = setupURL(m);
174 
175         LOGGER.info("Sending message to " + uri);
176         try
177         {
178             OutboundEndpoint ep = RegistryContext.getRegistry().lookupEndpointFactory().getOutboundEndpoint(uri);
179 
180             MessageAdapter req = (MessageAdapter) m.getExchange().get(CxfConstants.MULE_MESSAGE);
181             
182             MuleMessage result = sendStream(req, ep);
183 
184             // If we have a result, send it back to CXF
185             if (result != null && !isOneway(m.getExchange()))
186             {
187                 Message inMessage = new MessageImpl();
188                 String contentType = req.getStringProperty(HttpConstants.HEADER_CONTENT_TYPE, "text/xml");
189 
190                 inMessage.put(Message.ENCODING, result.getEncoding());
191                 inMessage.put(Message.CONTENT_TYPE, contentType);
192                 inMessage.setContent(InputStream.class, result.getPayload(InputStream.class));
193                 // inMessage.setContent(InputStream.class,
194                 // result.getPayload(InputStream.class));
195                 inMessage.setExchange(m.getExchange());
196                 getMessageObserver().onMessage(inMessage);
197             }
198         }
199         catch (Exception e)
200         {
201             if (e instanceof IOException)
202             {
203                 throw (IOException) e;
204             }
205 
206             IOException ex = new IOException("Could not send message to Mule.");
207             ex.initCause(e);
208             throw ex;
209         }
210     }
211 
212     private boolean isOneway(Exchange exchange)
213     {
214         return exchange != null && exchange.isOneWay();
215     }
216     
217     private String setupURL(Message message) throws MalformedURLException
218     {
219         String value = (String) message.get(Message.ENDPOINT_ADDRESS);
220         String pathInfo = (String) message.get(Message.PATH_INFO);
221         String queryString = (String) message.get(Message.QUERY_STRING);
222 
223         String result = value != null ? value : getTargetOrEndpoint();
224 
225         // REVISIT: is this really correct?
226         if (null != pathInfo && !result.endsWith(pathInfo))
227         {
228             result = result + pathInfo;
229         }
230         if (queryString != null)
231         {
232             result = result + "?" + queryString;
233         }
234         return result;
235     }
236 
237     private String getTargetOrEndpoint()
238     {
239         if (target != null)
240         {
241             return target.getAddress().getValue();
242         }
243 
244         return endpoint.getAddress().toString();
245     }
246 
247     public void onClose(final Message m) throws IOException
248     {
249     }
250     
251     protected MuleMessage sendStream(MessageAdapter sa, OutboundEndpoint ep) throws MuleException
252     {
253         MuleEventContext eventContext = RequestContext.getEventContext();
254         MuleSession session = null;
255         if (eventContext != null)
256         {
257             session = eventContext.getSession();
258         }
259 
260         MuleMessage message = new DefaultMuleMessage(sa);
261         if (session == null)
262         {
263             session = new DefaultMuleSession(message, connector.getSessionHandler(), connector.getMuleContext());
264         }
265 
266         MuleEvent event = new DefaultMuleEvent(message, ep, session, true);
267         event.setTimeout(MuleEvent.TIMEOUT_NOT_SET_VALUE);
268         RequestContext.setEvent(event);
269 
270         return ep.send(event);
271     }
272 
273     public void close()
274     {
275         // in decoupled case, close response Destination if reference count
276         // hits zero
277         //
278         if (decoupledDestination != null)
279         {
280             releaseDecoupledDestination();
281         }
282     }
283 
284     private synchronized void duplicateDecoupledDestination()
285     {
286         decoupledDestinationRefCount++;
287     }
288 
289     private synchronized void releaseDecoupledDestination()
290     {
291         if (--decoupledDestinationRefCount == 0)
292         {
293             // LOG.log(Level.INFO, "shutting down decoupled destination");
294             decoupledDestination.shutdown();
295         }
296     }
297 
298     public String getDecoupledEndpoint()
299     {
300         return decoupledEndpoint;
301     }
302 
303     public void setDecoupledEndpoint(String decoupledEndpoint)
304     {
305         this.decoupledEndpoint = decoupledEndpoint;
306     }
307 
308     /**
309      * Get the target endpoint reference.
310      * 
311      * @param ei the corresponding EndpointInfo
312      * @param t the given target EPR if available
313      * @param bus the Bus
314      * @return the actual target
315      */
316     protected static EndpointReferenceType getTargetReference(EndpointInfo ei, EndpointReferenceType t)
317     {
318         EndpointReferenceType ref = null;
319         if (null == t)
320         {
321             ref = new EndpointReferenceType();
322             AttributedURIType address = new AttributedURIType();
323             address.setValue(ei.getAddress());
324             ref.setAddress(address);
325             if (ei.getService() != null)
326             {
327                 EndpointReferenceUtils.setServiceAndPortName(ref, ei.getService().getName(), ei.getName()
328                     .getLocalPart());
329             }
330         }
331         else
332         {
333             ref = t;
334         }
335         return ref;
336     }
337 
338     /**
339      * Used to set appropriate message properties, exchange etc. as required for an
340      * incoming decoupled response (as opposed what's normally set by the Destination
341      * for an incoming request).
342      */
343     protected class InterposedMessageObserver implements MessageObserver
344     {
345         /**
346          * Called for an incoming message.
347          * 
348          * @param inMessage
349          */
350         public void onMessage(Message inMessage)
351         {
352             // disposable exchange, swapped with real Exchange on correlation
353             inMessage.setExchange(new ExchangeImpl());
354             inMessage.put(DECOUPLED_CHANNEL_MESSAGE, Boolean.TRUE);
355             inMessage.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);
356             inMessage.remove(Message.ASYNC_POST_RESPONSE_DISPATCH);
357 
358             incomingObserver.onMessage(inMessage);
359         }
360     }
361 }