View Javadoc

1   /*
2    * $Id: MuleClient.java 11775 2008-05-15 13:44:09Z dirk.olmes $
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.api.FutureMessageResult;
18  import org.mule.api.MessagingException;
19  import org.mule.api.MuleContext;
20  import org.mule.api.MuleEvent;
21  import org.mule.api.MuleException;
22  import org.mule.api.MuleMessage;
23  import org.mule.api.MuleSession;
24  import org.mule.api.config.ConfigurationBuilder;
25  import org.mule.api.config.ConfigurationException;
26  import org.mule.api.config.MuleConfiguration;
27  import org.mule.api.config.MuleProperties;
28  import org.mule.api.context.MuleContextBuilder;
29  import org.mule.api.endpoint.EndpointBuilder;
30  import org.mule.api.endpoint.EndpointURI;
31  import org.mule.api.endpoint.ImmutableEndpoint;
32  import org.mule.api.endpoint.InboundEndpoint;
33  import org.mule.api.endpoint.OutboundEndpoint;
34  import org.mule.api.lifecycle.Disposable;
35  import org.mule.api.lifecycle.InitialisationException;
36  import org.mule.api.registry.RegistrationException;
37  import org.mule.api.service.Service;
38  import org.mule.api.transport.DispatchException;
39  import org.mule.api.transport.ReceiveException;
40  import org.mule.config.DefaultMuleConfiguration;
41  import org.mule.config.i18n.CoreMessages;
42  import org.mule.config.spring.SpringXmlConfigurationBuilder;
43  import org.mule.context.DefaultMuleContextBuilder;
44  import org.mule.context.DefaultMuleContextFactory;
45  import org.mule.endpoint.EndpointURIEndpointBuilder;
46  import org.mule.endpoint.MuleEndpointURI;
47  import org.mule.module.client.i18n.ClientMessages;
48  import org.mule.security.MuleCredentials;
49  import org.mule.transformer.TransformerUtils;
50  import org.mule.transport.AbstractConnector;
51  import org.mule.transport.NullPayload;
52  import org.mule.util.StringUtils;
53  
54  import java.util.ArrayList;
55  import java.util.HashMap;
56  import java.util.Iterator;
57  import java.util.LinkedList;
58  import java.util.List;
59  import java.util.Map;
60  
61  import edu.emory.mathcs.backport.java.util.concurrent.Callable;
62  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
63  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
64  
65  import org.apache.commons.logging.Log;
66  import org.apache.commons.logging.LogFactory;
67  
68  /**
69   * <code>MuleClient</code> is a simple interface for Mule clients to send and
70   * receive events from a Mule Server. In most Mule applications events are triggered
71   * by some external occurrence such as a message being received on a queue or a file
72   * being copied to a directory. The Mule client allows the user to send and receive
73   * events programmatically through its API.
74   * <p>
75   * The client defines a {@link EndpointURI} which is used to determine how a message is
76   * sent of received. The url defines the protocol, the endpointUri destination of the
77   * message and optionally the endpoint to use when dispatching the event. For
78   * example:
79   * <p>
80   * <code>vm://my.object</code> dispatches to a <code>my.object</code> destination
81   * using the VM endpoint. There needs to be a global VM endpoint registered for the
82   * message to be sent.
83   * <p>
84   * <code>jms://jmsProvider/orders.topic</code> dispatches a JMS message via the
85   * globally registered jmsProvider over a topic destination called
86   * <code>orders.topic</code>.
87   * <p>
88   * <code>jms://orders.topic</code> is equivalent to the above except that the
89   * endpoint is determined by the protocol, so the first JMS endpoint is used.
90   * <p>
91   * Note that there must be a configured MuleManager for this client to work. It will
92   * use the one available using <code>muleContext</code>
93   * 
94   * @see org.mule.endpoint.MuleEndpointURI
95   */
96  public class MuleClient implements Disposable
97  {
98      /**
99       * logger used by this class
100      */
101     protected static final Log logger = LogFactory.getLog(MuleClient.class);
102 
103     /**
104      * the local UMOManager instance
105      */
106     private MuleContext muleContext;
107 
108     private List dispatchers = new ArrayList();
109 
110     private MuleCredentials user;
111 
112     private DefaultMuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
113     
114     private ConcurrentMap inboundEndpointCache = new ConcurrentHashMap();
115     private ConcurrentMap outboundEndpointCache = new ConcurrentHashMap();
116 
117     /**
118      * Creates a Mule client that will use the default serverEndpoint when connecting to a remote 
119      * server instance.
120      * 
121      * @throws MuleException
122      */
123     public MuleClient() throws MuleException
124     {
125         this(true);
126     }
127 
128     public MuleClient(boolean startContext) throws MuleException
129     {
130         init(startContext);
131     }
132 
133     public MuleClient(MuleContext context) throws MuleException
134     {
135         this.muleContext = context;
136         init(false);
137     }
138 
139     /**
140      * Configures a Mule client instance using the the default
141      * {@link SpringXmlConfigurationBuilder} to parse <code>configResources</code>.
142      * 
143      * @param configResources a config resource location to configure this client
144      *            with
145      * @throws ConfigurationException if there is a {@link MuleContext} instance already
146      *             running in this JVM or if the builder fails to configure the
147      *             Manager
148      */
149     public MuleClient(String configResources) throws MuleException
150     {
151         this(configResources, new SpringXmlConfigurationBuilder(configResources));
152     }
153 
154     /**
155      * Configures a new Mule client and either uses an existing Manager running in
156      * this JVM or creates a new empty {@link MuleContext}
157      * 
158      * @param user the username to use when connecting to a remote server instance
159      * @param password the password for the user
160      * @throws MuleException
161      */
162     public MuleClient(String user, String password) throws MuleException
163     {
164         init(/* startManager */true);
165         this.user = new MuleCredentials(user, password.toCharArray());
166     }
167 
168     /**
169      * Configures a Mule client instance
170      * 
171      * @param configResources a config resource location to configure this client
172      *            with
173      * @param builder the configuration builder to use
174      * @throws ConfigurationException is there is a {@link MuleContext} instance already
175      *             running in this JVM or if the builder fails to configure the
176      *             Manager
177      * @throws InitialisationException
178      */
179     public MuleClient(String configResources, ConfigurationBuilder builder)
180         throws ConfigurationException, InitialisationException
181     {
182         if (builder == null)
183         {
184             logger.info("Builder passed in was null, using default builder: "
185                         + SpringXmlConfigurationBuilder.class.getName());
186             builder = new SpringXmlConfigurationBuilder(configResources);
187         }
188         logger.info("Initializing Mule...");
189         muleContext = muleContextFactory.createMuleContext(builder);
190     }
191 
192     /**
193      * Configures a Mule client instance
194      * 
195      * @param configResources a config resource location to configure this client
196      *            with
197      * @param builder the configuration builder to use
198      * @param user the username to use when connecting to a remote server instance
199      * @param password the password for the user
200      * @throws ConfigurationException is there is a {@link MuleContext} instance already
201      *             running in this JVM or if the builder fails to configure the
202      *             Manager
203      * @throws InitialisationException
204      */
205     public MuleClient(String configResources, ConfigurationBuilder builder, String user, String password)
206         throws ConfigurationException, InitialisationException
207     {
208         this(configResources, builder);
209         this.user = new MuleCredentials(user, password.toCharArray());
210     }
211 
212     /**
213      * Initialises a default {@link MuleContext} for use by the client.
214      * 
215      * @param startManager start the Mule context if it has not yet been initialised
216      * @throws MuleException
217      */
218     private void init(boolean startManager) throws MuleException
219     {
220         // if we are creating a server for this client then set client mode
221         // this will disable Admin connections by default;
222         // If there is no local muleContext present create a default muleContext
223         if (muleContext == null)
224         {
225             muleContext = MuleServer.getMuleContext();
226         }
227         if (muleContext == null)
228         {
229             logger.info("No existing ManagementContext found, creating a new Mule instance");
230 
231             MuleContextBuilder contextBuilder = new DefaultMuleContextBuilder();
232             DefaultMuleConfiguration config = new DefaultMuleConfiguration();
233             config.setClientMode(true);
234             contextBuilder.setMuleConfiguration(config);
235             muleContext = muleContextFactory.createMuleContext(contextBuilder);
236         }
237         else
238         {
239             logger.info("Using existing MuleContext: " + muleContext);
240         }
241 
242         if (!muleContext.isStarted() && startManager == true)
243         {
244             logger.info("Starting Mule...");
245             muleContext.start();
246         }
247     }
248 
249     /**
250      * Dispatches an event asynchronously to a endpointUri via a Mule server. The URL
251      * determines where to dispatch the event to.
252      * 
253      * @param url the Mule URL used to determine the destination and transport of the
254      *            message
255      * @param payload the object that is the payload of the event
256      * @param messageProperties any properties to be associated with the payload. In
257      *            the case of JMS you could set the JMSReplyTo property in these
258      *            properties.
259      * @throws org.mule.api.MuleException
260      */
261     public void dispatch(String url, Object payload, Map messageProperties) throws MuleException
262     {
263         dispatch(url, new DefaultMuleMessage(payload, messageProperties));
264     }
265 
266     /**
267      * Dispatches an event asynchronously to a endpointUri via a Mule server. The URL
268      * determines where to dispatch the event to.
269      * 
270      * @param url the Mule URL used to determine the destination and transport of the
271      *            message
272      * @param message the message to send
273      * @throws org.mule.api.MuleException
274      */
275     public void dispatch(String url, MuleMessage message) throws MuleException
276     {
277         MuleEvent event = getEvent(message, url, false);
278         try
279         {
280             event.getSession().dispatchEvent(event);
281         }
282         catch (MuleException e)
283         {
284             throw e;
285         }
286         catch (Exception e)
287         {
288             throw new DispatchException(ClientMessages.failedToDispatchClientEvent(), event.getMessage(),
289                 event.getEndpoint(), e);
290         }
291     }
292 
293     /**
294      * Sends an event synchronously to a component
295      * 
296      * @param component the name of the Mule component to send to
297      * @param transformers a comma separated list of transformers to apply to the
298      *            result message
299      * @param payload the object that is the payload of the event
300      * @param messageProperties any properties to be associated with the payload. as
301      *            null
302      * @return the result message if any of the invocation
303      * @throws org.mule.api.MuleException if the dispatch fails or the components or
304      *             transfromers cannot be found
305      */
306     public MuleMessage sendDirect(String component, String transformers, Object payload, Map messageProperties)
307         throws MuleException
308     {
309         MuleMessage message = new DefaultMuleMessage(payload, messageProperties);
310         return sendDirect(component, transformers, message);
311     }
312 
313     /**
314      * Sends an event synchronously to a component
315      * 
316      * @param componentName the name of the Mule component to send to
317      * @param transformers a comma separated list of transformers to apply to the
318      *            result message
319      * @param message the message to send
320      * @return the result message if any of the invocation
321      * @throws org.mule.api.MuleException if the dispatch fails or the components or
322      *             transfromers cannot be found
323      */
324     public MuleMessage sendDirect(String componentName, String transformers, MuleMessage message)
325         throws MuleException
326     {
327         Service service = muleContext.getRegistry().lookupService(componentName);
328         if (service == null)
329         {
330             throw new MessagingException(CoreMessages.objectNotRegistered("Service", componentName),
331                 message);
332         }
333         List trans = null;
334         if (transformers != null)
335         {
336             trans = TransformerUtils.getTransformers(transformers);
337         }
338 
339         if (!muleContext.getConfiguration().isDefaultSynchronousEndpoints())
340         {
341             logger.warn("The mule muleContext is not running synchronously, a null message payload will be returned");
342         }
343         MuleSession session = new DefaultMuleSession(service, muleContext);
344         ImmutableEndpoint endpoint = getDefaultClientEndpoint(service, message.getPayload());
345         MuleEvent event = new DefaultMuleEvent(message, endpoint, session, true);
346 
347         if (logger.isDebugEnabled())
348         {
349             logger.debug("MuleClient sending event direct to: " + componentName + ". MuleEvent is: " + event);
350         }
351 
352         MuleMessage result = event.getService().sendEvent(event);
353 
354         if (logger.isDebugEnabled())
355         {
356             logger.debug("Result of MuleClient sendDirect is: "
357                          + (result == null ? "null" : result.getPayload()));
358         }
359 
360         if (result != null && trans != null)
361         {
362             result.applyTransformers(trans);
363         }
364         return result;
365     }
366 
367     /**
368      * Dispatches an event asynchronously to a component
369      * 
370      * @param component the name of the Mule components to dispatch to
371      * @param payload the object that is the payload of the event
372      * @param messageProperties any properties to be associated with the payload. as
373      *            null
374      * @throws org.mule.api.MuleException if the dispatch fails or the components or
375      *             transfromers cannot be found
376      */
377     public void dispatchDirect(String component, Object payload, Map messageProperties) throws MuleException
378     {
379         dispatchDirect(component, new DefaultMuleMessage(payload, messageProperties));
380     }
381 
382     /**
383      * Dispatches an event asynchronously to a component
384      * 
385      * @param componentName the name of the Mule components to dispatch to
386      * @param message the message to send
387      * @throws org.mule.api.MuleException if the dispatch fails or the components or
388      *             transfromers cannot be found
389      */
390     public void dispatchDirect(String componentName, MuleMessage message) throws MuleException
391     {
392         Service service = muleContext.getRegistry().lookupService(componentName);
393         if (service == null)
394         {
395             throw new MessagingException(CoreMessages.objectNotRegistered("Service", componentName),
396                 message);
397         }
398         MuleSession session = new DefaultMuleSession(service, muleContext);
399         ImmutableEndpoint endpoint = getDefaultClientEndpoint(service, message.getPayload());
400         MuleEvent event = new DefaultMuleEvent(message, endpoint, session, true);
401 
402         if (logger.isDebugEnabled())
403         {
404             logger.debug("MuleClient dispatching event direct to: " + componentName + ". MuleEvent is: " + event);
405         }
406 
407         event.getService().dispatchEvent(event);
408     }
409 
410     /**
411      * Sends an event request to a URL, making the result of the event trigger
412      * available as a Future result that can be accessed later by client code.
413      * 
414      * @param url the url to make a request on
415      * @param payload the object that is the payload of the event
416      * @param messageProperties any properties to be associated with the payload. as
417      *            null
418      * @return the result message if any of the invocation
419      * @throws org.mule.api.MuleException if the dispatch fails or the components or
420      *             transfromers cannot be found
421      */
422     public FutureMessageResult sendAsync(final String url, final Object payload, final Map messageProperties)
423         throws MuleException
424     {
425         return sendAsync(url, payload, messageProperties, 0);
426     }
427 
428     /**
429      * Sends an event request to a URL, making the result of the event trigger
430      * available as a Future result that can be accessed later by client code.
431      * 
432      * @param url the URL to make a request on
433      * @param message the message to send
434      * @return the result message if any of the invocation
435      * @throws org.mule.api.MuleException if the dispatch fails or the components or
436      *             transfromers cannot be found
437      */
438     public FutureMessageResult sendAsync(final String url, final MuleMessage message) throws MuleException
439     {
440         return sendAsync(url, message, MuleEvent.TIMEOUT_NOT_SET_VALUE);
441     }
442 
443     /**
444      * Sends an event request to a URL, making the result of the event trigger
445      * available as a Future result that can be accessed later by client code.
446      * 
447      * @param url the url to make a request on
448      * @param payload the object that is the payload of the event
449      * @param messageProperties any properties to be associated with the payload. as
450      *            null
451      * @param timeout how long to block in milliseconds waiting for a result
452      * @return the result message if any of the invocation
453      * @throws org.mule.api.MuleException if the dispatch fails or the components or
454      *             transfromers cannot be found
455      */
456     public FutureMessageResult sendAsync(final String url,
457                                          final Object payload,
458                                          final Map messageProperties,
459                                          final int timeout) throws MuleException
460     {
461         return sendAsync(url, new DefaultMuleMessage(payload, messageProperties), timeout);
462     }
463 
464     /**
465      * Sends an event request to a URL, making the result of the event trigger
466      * available as a Future result that can be accessed later by client code.
467      * 
468      * @param url the url to make a request on
469      * @param message the message to send
470      * @param timeout how long to block in milliseconds waiting for a result
471      * @return the result message if any of the invocation
472      * @throws org.mule.api.MuleException if the dispatch fails or the components or
473      *             transfromers cannot be found
474      */
475     public FutureMessageResult sendAsync(final String url, final MuleMessage message, final int timeout)
476         throws MuleException
477     {
478         Callable call = new Callable()
479         {
480             public Object call() throws Exception
481             {
482                 return send(url, message, timeout);
483             }
484         };
485 
486         FutureMessageResult result = new FutureMessageResult(call);
487 
488         if (muleContext.getWorkManager() != null)
489         {
490             result.setExecutor(muleContext.getWorkManager());
491         }
492 
493         result.execute();
494         return result;
495     }
496 
497     /**
498      * Sends an event to a component on a local Mule instance, while making the
499      * result of the event trigger available as a Future result that can be accessed
500      * later by client code. Users can specify a url to a remote Mule server in the
501      * constructor of a Mule client, by default the default Mule server url
502      * <code>tcp://localhost:60504</code> is used.
503      * 
504      * @param component the name of the Mule components to send to
505      * @param transformers a comma separated list of transformers to apply to the
506      *            result message
507      * @param payload the object that is the payload of the event
508      * @param messageProperties any properties to be associated with the payload.
509      * @return the result message if any of the invocation
510      * @throws org.mule.api.MuleException if the dispatch fails or the components or
511      *             transfromers cannot be found
512      */
513     public FutureMessageResult sendDirectAsync(final String component,
514                                                String transformers,
515                                                final Object payload,
516                                                final Map messageProperties) throws MuleException
517     {
518         return sendDirectAsync(component, transformers, new DefaultMuleMessage(payload, messageProperties));
519     }
520 
521     /**
522      * Snds an event to a component on a local Mule instance, while making the result
523      * of the event trigger available as a Future result that can be accessed later
524      * by client code. Users can specify a url to a remote Mule server in the
525      * constructor of a Mule client, by default the default Mule server url
526      * <code>tcp://localhost:60504</code> is used.
527      * 
528      * @param component the name of the Mule components to send to
529      * @param transformers a comma separated list of transformers to apply to the
530      *            result message
531      * @param message the message to send
532      * @return the result message if any of the invocation
533      * @throws org.mule.api.MuleException if the dispatch fails or the components or
534      *             transfromers cannot be found
535      */
536     public FutureMessageResult sendDirectAsync(final String component,
537                                                String transformers,
538                                                final MuleMessage message) throws MuleException
539     {
540         Callable call = new Callable()
541         {
542             public Object call() throws Exception
543             {
544                 return sendDirect(component, null, message);
545             }
546         };
547 
548         FutureMessageResult result = new FutureMessageResult(call);
549 
550         if (muleContext.getWorkManager() != null)
551         {
552             result.setExecutor(muleContext.getWorkManager());
553         }
554 
555         if (StringUtils.isNotBlank(transformers))
556         {
557             result.setTransformers(TransformerUtils.getTransformers(transformers));
558         }
559 
560         result.execute();
561         return result;
562     }
563 
564     /**
565      * Sends an event synchronously to a endpointUri via a Mule server and a
566      * resulting message is returned.
567      * 
568      * @param url the Mule URL used to determine the destination and transport of the
569      *            message
570      * @param payload the object that is the payload of the event
571      * @param messageProperties any properties to be associated with the payload. In
572      *            the case of Jms you could set the JMSReplyTo property in these
573      *            properties.
574      * @return A return message, this could be <code>null</code> if the the components invoked
575      *         explicitly sets a return as <code>null</code>.
576      * @throws org.mule.api.MuleException
577      */
578     public MuleMessage send(String url, Object payload, Map messageProperties) throws MuleException
579     {
580         return send(url, payload, messageProperties, MuleEvent.TIMEOUT_NOT_SET_VALUE);
581     }
582 
583     /**
584      * Sends an event synchronously to a endpointUri via a Mule server and a
585      * resulting message is returned.
586      * 
587      * @param url the Mule URL used to determine the destination and transport of the
588      *            message
589      * @param message the Message for the event
590      * @return A return message, this could be <code>null</code> if the the components invoked
591      *         explicitly sets a return as <code>null</code>.
592      * @throws org.mule.api.MuleException
593      */
594     public MuleMessage send(String url, MuleMessage message) throws MuleException
595     {
596         return send(url, message, MuleEvent.TIMEOUT_NOT_SET_VALUE);
597     }
598 
599     /**
600      * Sends an event synchronously to a endpointUri via a mule server and a
601      * resulting message is returned.
602      * 
603      * @param url the Mule URL used to determine the destination and transport of the
604      *            message
605      * @param payload the object that is the payload of the event
606      * @param messageProperties any properties to be associated with the payload. In
607      *            the case of Jms you could set the JMSReplyTo property in these
608      *            properties.
609      * @param timeout The time in milliseconds the the call should block waiting for
610      *            a response
611      * @return A return message, this could be <code>null</code> if the the components invoked
612      *         explicitly sets a return as <code>null</code>.
613      * @throws org.mule.api.MuleException
614      */
615     public MuleMessage send(String url, Object payload, Map messageProperties, int timeout)
616         throws MuleException
617     {
618         if (messageProperties == null)
619         {
620             messageProperties = new HashMap();
621         }
622         if (messageProperties.get(MuleProperties.MULE_REMOTE_SYNC_PROPERTY) == null)
623         {
624             messageProperties.put(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, "true");
625         }
626         MuleMessage message = new DefaultMuleMessage(payload, messageProperties);
627         return send(url, message, timeout);
628     }
629 
630     /**
631      * Sends an event synchronously to a endpointUri via a mule server and a
632      * resulting message is returned.
633      * 
634      * @param url the Mule URL used to determine the destination and transport of the
635      *            message
636      * @param message The message to send
637      * @param timeout The time in milliseconds the the call should block waiting for
638      *            a response
639      * @return A return message, this could be <code>null</code> if the the components invoked
640      *         explicitly sets a return as <code>null</code>.
641      * @throws org.mule.api.MuleException
642      */
643     public MuleMessage send(String url, MuleMessage message, int timeout) throws MuleException
644     {
645         MuleEvent event = getEvent(message, url, true);
646         event.setTimeout(timeout);
647 
648         try
649         {
650             MuleMessage msg = event.getSession().sendEvent(event);
651             if (msg == null)
652             {
653                 msg = new DefaultMuleMessage(NullPayload.getInstance());
654             }
655             return msg;
656         }
657         catch (MuleException e)
658         {
659             throw e;
660         }
661         catch (Exception e)
662         {
663             throw new DispatchException(ClientMessages.failedToDispatchClientEvent(), event.getMessage(),
664                 event.getEndpoint(), e);
665         }
666     }
667 
668     /**
669      * Will receive an event from an endpointUri determined by the URL.
670      * 
671      * @param url the Mule URL used to determine the destination and transport of the
672      *            message
673      * @param timeout how long to block waiting to receive the event, if set to 0 the
674      *            receive will not wait at all and if set to -1 the receive will wait
675      *            forever
676      * @return the message received or <code>null</code> if no message was received
677      * @throws org.mule.api.MuleException
678      */
679     public MuleMessage request(String url, long timeout) throws MuleException
680     {
681         InboundEndpoint endpoint = getInboundEndpoint(url);
682         try
683         {
684             MuleMessage message = endpoint.request(timeout);
685             if (message != null && endpoint.getTransformers() != null)
686             {
687                 message.applyTransformers(endpoint.getTransformers());
688             }
689             return message;
690         }
691         catch (Exception e)
692         {
693             throw new ReceiveException(endpoint, timeout, e);
694         }
695     }
696 
697     /**
698      * Will receive an event from an endpointUri determined by the URL
699      * 
700      * @param url the Mule URL used to determine the destination and transport of the
701      *            message
702      * @param transformers A comma separated list of transformers used to apply to
703      *            the result message
704      * @param timeout how long to block waiting to receive the event, if set to 0 the
705      *            receive will not wait at all and if set to -1 the receive will wait
706      *            forever
707      * @return the message received or <code>null</code> if no message was received
708      * @throws org.mule.api.MuleException
709      */
710     public MuleMessage request(String url, String transformers, long timeout) throws MuleException
711     {
712         return request(url, TransformerUtils.getTransformers(transformers), timeout);
713     }
714 
715     /**
716      * Will receive an event from an endpointUri determined by the URL
717      * 
718      * @param url the Mule URL used to determine the destination and transport of the
719      *            message
720      * @param transformers Transformers used to modify the result message
721      * @param timeout how long to block waiting to receive the event, if set to 0 the
722      *            receive will not wait at all and if set to -1 the receive will wait
723      *            forever
724      * @return the message received or <code>null</code> if no message was received
725      * @throws org.mule.api.MuleException
726      */
727     public MuleMessage request(String url, List transformers, long timeout) throws MuleException
728     {
729         MuleMessage message = request(url, timeout);
730         if (message != null && transformers != null)
731         {
732             message.applyTransformers(transformers);
733         }
734         return message;
735     }
736 
737     /**
738      * Packages a mule event for the current request
739      * 
740      * @param message the event payload
741      * @param uri the destination endpointUri
742      * @param synchronous whether the event will be synchronously processed
743      * @return the MuleEvent
744      * @throws MuleException
745      */
746     protected MuleEvent getEvent(MuleMessage message, String uri, boolean synchronous)
747         throws MuleException
748     {
749         ImmutableEndpoint endpoint = getOutboundEndpoint(uri);
750         if (!endpoint.getConnector().isStarted() && muleContext.isStarted())
751         {
752             endpoint.getConnector().start();
753         }
754         try
755         {
756             DefaultMuleSession session = new DefaultMuleSession(message,
757                 ((AbstractConnector) endpoint.getConnector()).getSessionHandler(), muleContext);
758 
759             if (user != null)
760             {
761                 message.setProperty(MuleProperties.MULE_USER_PROPERTY, MuleCredentials.createHeader(
762                     user.getUsername(), user.getPassword()));
763             }
764             DefaultMuleEvent event = new DefaultMuleEvent(message, endpoint, session, synchronous);
765             return event;
766         }
767         catch (Exception e)
768         {
769             throw new DispatchException(CoreMessages.failedToCreate("Client event"), message, endpoint, e);
770         }
771     }
772 
773     protected InboundEndpoint getInboundEndpoint(String uri) throws MuleException
774     {
775         // There was a potential leak here between get() and putIfAbsent(). This
776         // would cause the endpoint that was created to be used rather an endpoint
777         // with the same key that has been created and put in the cache by another
778         // thread. To avoid this we test for the result of putIfAbsent result and if
779         // it is non-null then an endpoint was created and added concurrently and we
780         // return this instance instead.
781         InboundEndpoint endpoint = (InboundEndpoint) inboundEndpointCache.get(uri);
782         if (endpoint == null)
783         {
784             endpoint = muleContext.getRegistry().lookupEndpointFactory().getInboundEndpoint(uri);
785             InboundEndpoint concurrentlyAddedEndpoint = (InboundEndpoint) inboundEndpointCache.putIfAbsent(uri, endpoint);
786             if (concurrentlyAddedEndpoint != null)
787             {
788                 return concurrentlyAddedEndpoint;
789             }
790         }
791         return endpoint;
792     }
793 
794     protected OutboundEndpoint getOutboundEndpoint(String uri) throws MuleException
795     {
796         // There was a potential leak here between get() and putIfAbsent(). This
797         // would cause the endpoint that was created to be used rather an endpoint
798         // with the same key that has been created and put in the cache by another
799         // thread. To avoid this we test for the result of putIfAbsent result and if
800         // it is non-null then an endpoint was created and added concurrently and we
801         // return this instance instead.
802         OutboundEndpoint endpoint = (OutboundEndpoint) outboundEndpointCache.get(uri);
803         if (endpoint == null)
804         {
805             endpoint = muleContext.getRegistry().lookupEndpointFactory().getOutboundEndpoint(uri);
806             OutboundEndpoint concurrentlyAddedEndpoint = (OutboundEndpoint) outboundEndpointCache.putIfAbsent(uri, endpoint);
807             if (concurrentlyAddedEndpoint != null)
808             {
809                 return concurrentlyAddedEndpoint;
810             }
811         }
812         return endpoint;
813     }
814 
815     protected ImmutableEndpoint getDefaultClientEndpoint(Service service, Object payload)
816         throws MuleException
817     {
818         // as we are bypassing the message transport layer we need to check that
819         ImmutableEndpoint endpoint = (ImmutableEndpoint) service.getInboundRouter().getEndpoints().get(0);
820         if (endpoint != null)
821         {
822             if (endpoint.getTransformers() != null)
823             {
824                 // the original code here really did just check the first exception
825                 // as far as i can tell
826                 if (TransformerUtils.isSourceTypeSupportedByFirst(endpoint.getTransformers(),
827                     payload.getClass()))
828                 {
829                     return endpoint;
830                 }
831                 else
832                 {
833                     EndpointBuilder builder = new EndpointURIEndpointBuilder(endpoint, muleContext);
834                     builder.setTransformers(new LinkedList());
835                     return muleContext.getRegistry().lookupEndpointFactory().getInboundEndpoint(builder);
836                 }
837             }
838             else
839             {
840                 return endpoint;
841             }
842         }
843         else
844         {
845             EndpointBuilder builder = new EndpointURIEndpointBuilder("vm://mule.client", muleContext);
846             builder.setName("muleClientProvider");
847             endpoint = muleContext.getRegistry().lookupEndpointFactory().getInboundEndpoint(builder);
848         }
849         return endpoint;
850     }
851 
852     /**
853      * Sends an event synchronously to a endpointUri via a Mule server without
854      * waiting for the result.
855      * 
856      * @param url the Mule URL used to determine the destination and transport of the
857      *            message
858      * @param payload the object that is the payload of the event
859      * @param messageProperties any properties to be associated with the payload. In
860      *            the case of Jms you could set the JMSReplyTo property in these
861      *            properties.
862      * @throws org.mule.api.MuleException
863      */
864     public void sendNoReceive(String url, Object payload, Map messageProperties) throws MuleException
865     {
866         if (messageProperties == null)
867         {
868             messageProperties = new HashMap();
869         }
870         messageProperties.put(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, "false");
871         MuleMessage message = new DefaultMuleMessage(payload, messageProperties);
872         MuleEvent event = getEvent(message, url, true);
873         try
874         {
875             event.getSession().sendEvent(event);
876         }
877         catch (MuleException e)
878         {
879             throw e;
880         }
881         catch (Exception e)
882         {
883             throw new DispatchException(ClientMessages.failedToDispatchClientEvent(), event.getMessage(),
884                 event.getEndpoint(), e);
885         }
886     }
887 
888     /**
889      * The overriding method may want to return a custom {@link MuleContext} here
890      * 
891      * @return the UMOManager to use
892      */
893     public MuleContext getMuleContext()
894     {
895         return muleContext;
896     }
897 
898     /**
899      * Registers a Java object as a component that listens for events on the
900      * given URL. By default the ThreadingProfile for the components will be set so
901      * that there will only be one thread of execution.
902      * 
903      * @param component any java object, Mule will it's endpointUri discovery to
904      *            determine which event to invoke based on the evnet payload type
905      * @param name The identifying name of the components. This can be used to later
906      *            unregister it
907      * @param listenerEndpoint The url endpointUri to listen to
908      * @throws MuleException
909      * @deprecated Use the RegistryContext to get the registry and register the
910      *             service there
911      */
912     public void registerComponent(Object component, String name, EndpointURI listenerEndpoint)
913         throws MuleException
914     {
915         throw new UnsupportedOperationException("registerComponent");
916         // builder.registerComponentInstance(service, name, listenerEndpoint,
917         // null);
918     }
919 
920     /**
921      * Registers a Java object as a component that listens for and sends events
922      * on the given urls. By default the ThreadingProfile for the components will be
923      * set so that there will only be one thread of execution.
924      * 
925      * @param component any java object, Mule will it's endpointUri discovery to
926      *            determine which event to invoke based on the evnet payload type
927      * @param name The identifying name of the components. This can be used to later
928      *            unregister it
929      * @param listenerEndpoint The url endpointUri to listen to
930      * @param sendEndpoint The url endpointUri to dispatch to
931      * @throws MuleException
932      * @deprecated Use the RegistryContext to get the registry and register the
933      *             service there
934      */
935     public void registerComponent(Object component,
936                                   String name,
937                                   MuleEndpointURI listenerEndpoint,
938                                   MuleEndpointURI sendEndpoint) throws MuleException
939     {
940         throw new UnsupportedOperationException("registerComponent");
941         // builder.registerComponentInstance(service, name, listenerEndpoint,
942         // sendEndpoint);
943     }
944 
945     /**
946      * Registers a user configured MuleDescriptor of a components to the server. If
947      * users want to register object instances with the server rather than class
948      * names that get created at runtime or reference to objects in the container,
949      * the user must call the descriptors setImplementationInstance() method - <code>
950      * MyBean implementation = new MyBean();
951      * descriptor.setImplementationInstance(implementation);
952      * </code>
953      * Calling this method is equivilent to calling Model.registerComponent(..)
954      * 
955      * @param descriptor the componet descriptor to register
956      * @throws MuleException the descriptor is invalid or cannot be initialised or
957      *             started
958      * @see org.mule.api.model.Model
959      * @deprecated Use the RegistryContext to get the registry and register the
960      *             service there
961      */
962     // public void registerComponent(UMODescriptor descriptor) throws MuleException
963     // {
964     // throw new UnsupportedOperationException("registerComponent");
965     // //builder.registerComponent(descriptor);
966     // }
967     /**
968      * Unregisters a previously register components. This will also unregister any
969      * listeners for the components Calling this method is equivilent to calling
970      * Model.unregisterComponent(..)
971      * 
972      * @param name the name of the componet to unregister
973      * @throws MuleException if unregistering the components fails, i.e. The
974      *             underlying transport fails to unregister a listener. If the
975      *             components does not exist, this method should not throw an
976      *             exception.
977      * @see org.mule.api.model.Model
978      * @deprecated Use the RegistryContext to get the registry and unregister the
979      *             service there
980      */
981     public void unregisterComponent(String name) throws MuleException
982     {
983         throw new UnsupportedOperationException("registerComponent");
984 
985         // builder.unregisterComponent(name);
986     }
987 
988     public RemoteDispatcher getRemoteDispatcher(String serverEndpoint) throws MuleException
989     {
990         RemoteDispatcher rd = new RemoteDispatcher(serverEndpoint);
991         rd.setExecutor(muleContext.getWorkManager());
992         dispatchers.add(rd);
993         return rd;
994     }
995 
996     public RemoteDispatcher getRemoteDispatcher(String serverEndpoint, String user, String password)
997         throws MuleException
998     {
999         RemoteDispatcher rd = new RemoteDispatcher(serverEndpoint, new MuleCredentials(user,
1000             password.toCharArray()));
1001         rd.setExecutor(muleContext.getWorkManager());
1002         dispatchers.add(rd);
1003         return rd;
1004     }
1005 
1006     /**
1007      * Will dispose the MuleManager instance <b>if</b> a new instance was created for this
1008      * client. Otherwise this method only cleans up resources no longer needed
1009      */
1010     public void dispose()
1011     {
1012         synchronized (dispatchers)
1013         {
1014             for (Iterator iterator = dispatchers.iterator(); iterator.hasNext();)
1015             {
1016                 RemoteDispatcher remoteDispatcher = (RemoteDispatcher) iterator.next();
1017                 remoteDispatcher.dispose();
1018                 remoteDispatcher = null;
1019             }
1020             dispatchers.clear();
1021         }
1022         // Dispose the muleContext only if the muleContext was created for this
1023         // client
1024         if (muleContext.getConfiguration().isClientMode())
1025         {
1026             logger.info("Stopping Mule...");
1027             muleContext.dispose();
1028         }
1029     }
1030 
1031     public void setProperty(String key, Object value)
1032     {
1033         try
1034         {
1035             muleContext.getRegistry().registerObject(key, value);
1036         }
1037         catch (RegistrationException e)
1038         {
1039             logger.error(e);
1040         }
1041     }
1042 
1043     public Object getProperty(String key)
1044     {
1045         return muleContext.getRegistry().lookupObject(key);
1046     }
1047 
1048     public MuleConfiguration getConfiguration()
1049     {
1050         return muleContext.getConfiguration();
1051     }
1052 }