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