View Javadoc

1   /*
2    * $Id: RemoteDispatcher.java 20385 2010-11-29 20:25:26Z dfeist $
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.client;
12  
13  import org.mule.DefaultMuleEvent;
14  import org.mule.DefaultMuleMessage;
15  import org.mule.MessageExchangePattern;
16  import org.mule.RequestContext;
17  import org.mule.api.FutureMessageResult;
18  import org.mule.api.MuleContext;
19  import org.mule.api.MuleEvent;
20  import org.mule.api.MuleException;
21  import org.mule.api.MuleMessage;
22  import org.mule.api.MuleSession;
23  import org.mule.api.config.MuleProperties;
24  import org.mule.api.endpoint.EndpointBuilder;
25  import org.mule.api.endpoint.EndpointFactory;
26  import org.mule.api.endpoint.ImmutableEndpoint;
27  import org.mule.api.endpoint.OutboundEndpoint;
28  import org.mule.api.lifecycle.Disposable;
29  import org.mule.api.security.Credentials;
30  import org.mule.api.transformer.wire.WireFormat;
31  import org.mule.api.transport.DispatchException;
32  import org.mule.client.DefaultLocalMuleClient.MuleClientFlowConstruct;
33  import org.mule.module.client.i18n.ClientMessages;
34  import org.mule.module.client.remoting.RemoteDispatcherException;
35  import org.mule.module.client.remoting.ServerHandshake;
36  import org.mule.module.client.remoting.UnsupportedWireFormatException;
37  import org.mule.module.client.remoting.notification.RemoteDispatcherNotification;
38  import org.mule.security.MuleCredentials;
39  import org.mule.session.DefaultMuleSession;
40  import org.mule.transformer.TransformerUtils;
41  import org.mule.transport.NullPayload;
42  import org.mule.util.ClassUtils;
43  import org.mule.util.IOUtils;
44  
45  import java.io.ByteArrayInputStream;
46  import java.io.ByteArrayOutputStream;
47  import java.io.InputStream;
48  import java.util.Map;
49  
50  import edu.emory.mathcs.backport.java.util.concurrent.Callable;
51  import edu.emory.mathcs.backport.java.util.concurrent.Executor;
52  
53  import org.apache.commons.lang.SerializationUtils;
54  import org.apache.commons.logging.Log;
55  import org.apache.commons.logging.LogFactory;
56  
57  /**
58   * <code>RemoteDispatcher</code> is used to make and receive requests to a remote
59   * Mule instance. It is used to proxy requests to Mule using the Server URL as the
60   * transport channel.
61   */
62  
63  public class RemoteDispatcher implements Disposable
64  {
65  
66      /**
67       * logger used by this class
68       */
69      protected static final Log logger = LogFactory.getLog(RemoteDispatcher.class);
70  
71      /**
72       * dispatch destination
73       */
74      private OutboundEndpoint asyncServerEndpoint;
75      private OutboundEndpoint syncServerEndpoint;
76      private Credentials credentials = null;
77      private MuleContext muleContext;
78  
79      /**
80       * an ExecutorService for async messages (optional)
81       */
82      private Executor asyncExecutor;
83  
84      /**
85       * calls made to a remote server are serialised using a wireformat
86       */
87      private WireFormat wireFormat;
88  
89      protected RemoteDispatcher(String endpoint, Credentials credentials, MuleContext muleContext) throws MuleException
90      {
91          this(endpoint, muleContext);
92          this.credentials = credentials;
93      }
94  
95      protected RemoteDispatcher(String endpoint, MuleContext muleContext) throws MuleException
96      {
97          this.muleContext = muleContext;
98          EndpointFactory endpointFactory = muleContext.getRegistry().lookupEndpointFactory();
99          asyncServerEndpoint = endpointFactory.getOutboundEndpoint(endpoint);
100 
101         EndpointBuilder endpointBuilder = endpointFactory.getEndpointBuilder(endpoint);
102         endpointBuilder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
103         syncServerEndpoint = muleContext.getEndpointFactory().getOutboundEndpoint(
104             endpointBuilder);
105 
106         wireFormat = requestWireFormat();
107     }
108 
109     protected WireFormat requestWireFormat() throws MuleException
110     {
111         MuleMessage msg = new DefaultMuleMessage(ServerHandshake.SERVER_HANDSHAKE_PROPERTY, muleContext);
112         MuleMessage result = null;
113         
114         MuleEvent resultEvent = syncServerEndpoint.process(new DefaultMuleEvent(msg, syncServerEndpoint,
115             new DefaultMuleSession(new MuleClientFlowConstruct(muleContext), muleContext)));
116         if (resultEvent != null)
117         {
118             result = resultEvent.getMessage();
119         }
120 
121         if(result==null)
122         {
123             throw new RemoteDispatcherException(ClientMessages.failedToDispatchActionNoResponseFromServer("request wire format", 5000));
124         }
125 
126         ServerHandshake handshake;
127         try
128         {
129             ByteArrayInputStream in = new ByteArrayInputStream(result.getPayloadAsBytes());
130             handshake = (ServerHandshake) SerializationUtils.deserialize(in);
131         }
132         catch (Exception e)
133         {
134             throw new RemoteDispatcherException(ClientMessages.failedToDeserializeHandshakeFromServer(), e);
135         }
136 
137         try
138         {
139             WireFormat wf = (WireFormat)ClassUtils.instanciateClass(handshake.getWireFormatClass(),
140                     ClassUtils.NO_ARGS, getClass());
141 
142             wf.setMuleContext(muleContext);
143             return wf;
144         }
145         catch (Exception e)
146         {
147             throw new UnsupportedWireFormatException(handshake.getWireFormatClass(), e);
148         }
149     }
150 
151     protected void setExecutor(Executor e)
152     {
153         this.asyncExecutor = e;
154     }
155 
156     /**
157      * Dispatcher an event asynchronously to a components on a remote Mule instance.
158      * Users can endpoint a url to a remote Mule server in the constructor of a Mule
159      * client, by default the default Mule server url tcp://localhost:60504 is used.
160      *
161      * @param component the name of the Mule components to dispatch to
162      * @param payload the object that is the payload of the event
163      * @param messageProperties any properties to be associated with the payload. as
164      *            null
165      * @throws org.mule.api.MuleException if the dispatch fails or the components or
166      *             transfromers cannot be found
167      */
168     public void dispatchToRemoteComponent(String component, Object payload, Map messageProperties)
169         throws MuleException
170     {
171         doToRemoteComponent(component, payload, messageProperties, false);
172     }
173 
174     /**
175      * sends an event synchronously to a components on a remote Mule instance. Users
176      * can endpoint a url to a remote Mule server in the constructor of a Mule
177      * client, by default the default Mule server url tcp://localhost:60504 is used.
178      *
179      * @param component the name of the Mule components to send to
180      * @param payload the object that is the payload of the event
181      * @param messageProperties any properties to be associated with the payload. as
182      *            null
183      * @return the result message if any of the invocation
184      * @throws org.mule.api.MuleException if the dispatch fails or the components or
185      *             transfromers cannot be found
186      */
187     public MuleMessage sendToRemoteComponent(String component, Object payload, Map messageProperties)
188         throws MuleException
189     {
190         return doToRemoteComponent(component, payload, messageProperties, true);
191     }
192 
193     /**
194      * sends an event to a components on a remote Mule instance, while making the
195      * result of the event trigger available as a Future result that can be accessed
196      * later by client code. Users can endpoint a url to a remote Mule server in the
197      * constructor of a Mule client, by default the default Mule server url
198      * tcp://localhost:60504 is used.
199      *
200      * @param component the name of the Mule components to send to
201      * @param transformers a comma separated list of transformers to apply to the
202      *            result message
203      * @param payload the object that is the payload of the event
204      * @param messageProperties any properties to be associated with the payload. as
205      *            null
206      * @return the result message if any of the invocation
207      * @throws org.mule.api.MuleException if the dispatch fails or the components or
208      *             transfromers cannot be found
209      */
210     public FutureMessageResult sendAsyncToRemoteComponent(final String component,
211                                                           String transformers,
212                                                           final Object payload,
213                                                           final Map messageProperties) throws MuleException
214     {
215         Callable callable = new Callable()
216         {
217             public Object call() throws Exception
218             {
219                 return doToRemoteComponent(component, payload, messageProperties, true);
220             }
221         };
222 
223         FutureMessageResult result = new FutureMessageResult(callable, muleContext);
224 
225         if (asyncExecutor != null)
226         {
227             result.setExecutor(asyncExecutor);
228         }
229 
230         if (transformers != null)
231         {
232             result.setTransformers(TransformerUtils.getTransformers(transformers, muleContext));
233         }
234 
235         result.execute();
236         return result;
237     }
238 
239     public MuleMessage sendRemote(String endpoint, Object payload, Map messageProperties, int timeout)
240         throws MuleException
241     {
242         return doToRemote(endpoint, payload, messageProperties, true, timeout);
243     }
244 
245     public MuleMessage sendRemote(String endpoint, Object payload, Map messageProperties) throws MuleException
246     {
247         return doToRemote(endpoint, payload, messageProperties, true, 
248             muleContext.getConfiguration().getDefaultResponseTimeout());
249     }
250 
251     public void dispatchRemote(String endpoint, Object payload, Map messageProperties) throws MuleException
252     {
253         doToRemote(endpoint, payload, messageProperties, false, -1);
254     }
255 
256     public FutureMessageResult sendAsyncRemote(final String endpoint,
257                                                final Object payload,
258                                                final Map messageProperties) throws MuleException
259     {
260         Callable callable = new Callable()
261         {
262             public Object call() throws Exception
263             {
264                 return doToRemote(endpoint, payload, messageProperties, true, -1);
265             }
266         };
267 
268         FutureMessageResult result = new FutureMessageResult(callable, muleContext);
269 
270         if (asyncExecutor != null)
271         {
272             result.setExecutor(asyncExecutor);
273         }
274 
275         result.execute();
276         return result;
277     }
278 
279     public MuleMessage receiveRemote(String endpoint, int timeout) throws MuleException
280     {
281         RemoteDispatcherNotification action = new RemoteDispatcherNotification(null, RemoteDispatcherNotification.ACTION_RECEIVE, endpoint);
282         action.setProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, "true");
283         if (timeout != MuleEvent.TIMEOUT_NOT_SET_VALUE)
284         {
285             action.setProperty(MuleProperties.MULE_EVENT_TIMEOUT_PROPERTY, new Long(timeout));
286         }
287         return dispatchAction(action, true, timeout);
288     }
289 
290     public FutureMessageResult asyncReceiveRemote(final String endpoint, final int timeout)
291         throws MuleException
292     {
293         Callable callable = new Callable()
294         {
295             public Object call() throws Exception
296             {
297                 return receiveRemote(endpoint, timeout);
298             }
299         };
300 
301         FutureMessageResult result = new FutureMessageResult(callable, muleContext);
302 
303         if (asyncExecutor != null)
304         {
305             result.setExecutor(asyncExecutor);
306         }
307 
308         result.execute();
309         return result;
310     }
311 
312     protected MuleMessage doToRemoteComponent(String component,
313                                              Object payload,
314                                              Map messageProperties,
315                                              boolean synchronous) throws MuleException
316     {
317         MuleMessage message = new DefaultMuleMessage(payload, messageProperties, muleContext);
318         message.setOutboundProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, synchronous);
319         setCredentials(message);
320         RemoteDispatcherNotification action = new RemoteDispatcherNotification(message, RemoteDispatcherNotification.ACTION_INVOKE,
321             "mule://" + component);
322         return dispatchAction(action, synchronous, 
323             muleContext.getConfiguration().getDefaultResponseTimeout());
324     }
325 
326     protected MuleMessage doToRemote(String endpoint,
327                                     Object payload,
328                                     Map messageProperties,
329                                     boolean synchronous,
330                                     int timeout) throws MuleException
331     {
332         MuleMessage message = new DefaultMuleMessage(payload, messageProperties, muleContext);
333         message.setOutboundProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, String.valueOf(synchronous));
334         setCredentials(message);
335         RemoteDispatcherNotification action = new RemoteDispatcherNotification(message, (synchronous
336                         ? RemoteDispatcherNotification.ACTION_SEND : RemoteDispatcherNotification.ACTION_DISPATCH), endpoint);
337 
338         return dispatchAction(action, synchronous, timeout);
339     }
340 
341     protected MuleMessage dispatchAction(RemoteDispatcherNotification action, boolean synchronous, int timeout)
342         throws MuleException
343     {
344         OutboundEndpoint serverEndpoint;
345         if (synchronous)
346         {
347             serverEndpoint = syncServerEndpoint;
348         }
349         else
350         {
351             serverEndpoint = asyncServerEndpoint;
352         }
353         MuleMessage serializeMessage = new DefaultMuleMessage(action, muleContext);
354         
355         updateContext(serializeMessage, serverEndpoint, synchronous);
356 
357         ByteArrayOutputStream out = new ByteArrayOutputStream();
358         wireFormat.write(out, serializeMessage, serverEndpoint.getEncoding());
359         byte[] payload = out.toByteArray();
360 
361         MuleMessage message = action.getMessage();
362 
363         if (message == null)
364         {
365             message = new DefaultMuleMessage(payload, muleContext);
366         }
367         else
368         {
369             message = new DefaultMuleMessage(payload, message, muleContext);
370         }
371 
372         message.addProperties(action.getProperties());
373         MuleSession session = new DefaultMuleSession(muleContext);
374 
375         MuleEvent event = new DefaultMuleEvent(message, serverEndpoint, session);
376         event.setTimeout(timeout);
377         if (logger.isDebugEnabled())
378         {
379             logger.debug("MuleClient sending remote call to: " + action.getResourceIdentifier() + ". At "
380                          + serverEndpoint.toString() + " . Event is: " + event);
381         }
382 
383 
384         MuleMessage result = null;
385 
386         try
387         {
388             MuleEvent resultEvent = serverEndpoint.process(event);
389             if (resultEvent != null)
390             {
391                 result = resultEvent.getMessage();
392             }
393 
394             if (result != null && result.getPayload() != null)
395             {
396                 if (result.getPayload() instanceof NullPayload)
397                 {
398                     return null;
399                 }
400                 
401                 Object response;
402                 if (result.getPayload() instanceof InputStream)
403                 {
404                     byte[] b = IOUtils.toByteArray((InputStream)result.getPayload());
405                     if(b.length==0) return null;
406                     ByteArrayInputStream in = new ByteArrayInputStream(b);
407                     response = wireFormat.read(in);
408                 }
409                 else
410                 {
411                     ByteArrayInputStream in = new ByteArrayInputStream(result.getPayloadAsBytes());
412                     response = wireFormat.read(in);
413                 }
414 
415                 if (response instanceof RemoteDispatcherNotification)
416                 {
417                     response = ((RemoteDispatcherNotification)response).getMessage();
418                 }
419                 return (MuleMessage)response;
420             }
421         }
422         catch (Exception e)
423         {
424             throw new DispatchException(event, serverEndpoint, e);
425         }
426 
427         if (logger.isDebugEnabled())
428         {
429             logger.debug("Result of MuleClient remote call is: "
430                          + (result == null ? "null" : result.getPayload()));
431         }
432 
433         return result;
434     }
435 
436     public void dispose()
437     {
438         // nothing to do here
439     }
440 
441     protected void setCredentials(MuleMessage message)
442     {
443         if (credentials != null)
444         {
445             message.setOutboundProperty(MuleProperties.MULE_USER_PROPERTY, MuleCredentials.createHeader(
446                     credentials.getUsername(), credentials.getPassword()));
447         }
448     }
449 
450     public WireFormat getWireFormat()
451     {
452         return wireFormat;
453     }
454 
455     public void setWireFormat(WireFormat wireFormat)
456     {
457         this.wireFormat = wireFormat;
458     }
459 
460     protected void updateContext(MuleMessage message, ImmutableEndpoint endpoint, boolean synchronous)
461         throws MuleException
462     {
463         RequestContext.setEvent(new DefaultMuleEvent(message, endpoint, new DefaultMuleSession(muleContext)));
464     }
465 }