View Javadoc

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