View Javadoc

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