View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.module.client;
8   
9   import org.mule.DefaultMuleEvent;
10  import org.mule.DefaultMuleMessage;
11  import org.mule.MessageExchangePattern;
12  import org.mule.api.FutureMessageResult;
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleException;
16  import org.mule.api.MuleMessage;
17  import org.mule.api.MuleSession;
18  import org.mule.api.config.ConfigurationBuilder;
19  import org.mule.api.config.ConfigurationException;
20  import org.mule.api.config.MuleConfiguration;
21  import org.mule.api.config.MuleProperties;
22  import org.mule.api.context.MuleContextBuilder;
23  import org.mule.api.endpoint.EndpointBuilder;
24  import org.mule.api.endpoint.EndpointURI;
25  import org.mule.api.endpoint.ImmutableEndpoint;
26  import org.mule.api.endpoint.InboundEndpoint;
27  import org.mule.api.endpoint.OutboundEndpoint;
28  import org.mule.api.lifecycle.Disposable;
29  import org.mule.api.lifecycle.InitialisationException;
30  import org.mule.api.registry.RegistrationException;
31  import org.mule.api.registry.ServiceException;
32  import org.mule.api.service.Service;
33  import org.mule.api.transformer.Transformer;
34  import org.mule.api.transport.DispatchException;
35  import org.mule.api.transport.ReceiveException;
36  import org.mule.client.DefaultLocalMuleClient;
37  import org.mule.config.DefaultMuleConfiguration;
38  import org.mule.config.i18n.CoreMessages;
39  import org.mule.config.spring.SpringXmlConfigurationBuilder;
40  import org.mule.context.DefaultMuleContextBuilder;
41  import org.mule.context.DefaultMuleContextFactory;
42  import org.mule.endpoint.EndpointURIEndpointBuilder;
43  import org.mule.endpoint.MuleEndpointURI;
44  import org.mule.module.client.i18n.ClientMessages;
45  import org.mule.security.MuleCredentials;
46  import org.mule.service.ServiceCompositeMessageSource;
47  import org.mule.session.DefaultMuleSession;
48  import org.mule.transformer.TransformerUtils;
49  import org.mule.transport.NullPayload;
50  import org.mule.util.StringUtils;
51  
52  import java.util.ArrayList;
53  import java.util.HashMap;
54  import java.util.Iterator;
55  import java.util.LinkedList;
56  import java.util.List;
57  import java.util.Map;
58  
59  import edu.emory.mathcs.backport.java.util.concurrent.Callable;
60  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
61  import edu.emory.mathcs.backport.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, endpoint);
270         try
271         {
272             endpoint.process(event);
273         }
274         catch (MuleException e)
275         {
276             throw e;
277         }
278         catch (Exception e)
279         {
280             throw new DispatchException(ClientMessages.failedToDispatchClientEvent(), event,
281                 endpoint, e);
282         }
283     }
284 
285     /**
286      * Sends an event synchronously to a component
287      * 
288      * @param component the name of the Mule component to send to
289      * @param transformers a comma separated list of transformers to apply to the
290      *            result message
291      * @param payload the object that is the payload of the event
292      * @param messageProperties any properties to be associated with the payload. as
293      *            null
294      * @return the result message if any of the invocation
295      * @throws org.mule.api.MuleException if the dispatch fails or the components or
296      *             transfromers cannot be found
297      */
298     public MuleMessage sendDirect(String component, String transformers, Object payload, Map messageProperties)
299         throws MuleException
300     {
301         MuleMessage message = new DefaultMuleMessage(payload, messageProperties, muleContext);
302         return sendDirect(component, transformers, message);
303     }
304 
305     /**
306      * Sends an event synchronously to a component
307      * 
308      * @param componentName the name of the Mule component to send to
309      * @param transformers a comma separated list of transformers to apply to the
310      *            result message
311      * @param message the message to send
312      * @return the result message if any of the invocation
313      * @throws org.mule.api.MuleException if the dispatch fails or the components or
314      *             transfromers cannot be found
315      */
316     public MuleMessage sendDirect(String componentName, String transformers, MuleMessage message)
317         throws MuleException
318     {
319         Service service = muleContext.getRegistry().lookupService(componentName);
320         if (service == null)
321         {
322             throw new ServiceException(CoreMessages.objectNotRegistered("Service", componentName));
323         }
324         List<Transformer> trans = null;
325         if (transformers != null)
326         {
327             trans = TransformerUtils.getTransformers(transformers, muleContext);
328         }
329 
330         MuleSession session = new DefaultMuleSession(service, muleContext);
331         ImmutableEndpoint endpoint = getDefaultClientEndpoint(service, message.getPayload(), true);
332         MuleEvent event = new DefaultMuleEvent(message, endpoint, session);
333 
334         if (logger.isDebugEnabled())
335         {
336             logger.debug("MuleClient sending event direct to: " + componentName + ". MuleEvent is: " + event);
337         }
338 
339         MuleEvent resultEvent = service.sendEvent(event);
340         MuleMessage result = resultEvent == null ? null : resultEvent.getMessage();
341         if (logger.isDebugEnabled())
342         {
343             logger.debug("Result of MuleClient sendDirect is: "
344                          + (result == null ? "null" : result.getPayload()));
345         }
346 
347         if (result != null && trans != null)
348         {
349             result.applyTransformers(resultEvent, trans);
350         }
351         return result;
352     }
353 
354     /**
355      * Dispatches an event asynchronously to a component
356      * 
357      * @param component the name of the Mule components to dispatch to
358      * @param payload the object that is the payload of the event
359      * @param messageProperties any properties to be associated with the payload. as
360      *            null
361      * @throws org.mule.api.MuleException if the dispatch fails or the components or
362      *             transfromers cannot be found
363      */
364     public void dispatchDirect(String component, Object payload, Map messageProperties) throws MuleException
365     {
366         dispatchDirect(component, new DefaultMuleMessage(payload, messageProperties, muleContext));
367     }
368 
369     /**
370      * Dispatches an event asynchronously to a component
371      * 
372      * @param componentName the name of the Mule components to dispatch to
373      * @param message the message to send
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 componentName, MuleMessage message) throws MuleException
378     {
379         Service service = muleContext.getRegistry().lookupService(componentName);
380         if (service == null)
381         {
382             throw new ServiceException(CoreMessages.objectNotRegistered("Service", componentName));
383         }
384         MuleSession session = new DefaultMuleSession(service, muleContext);
385         ImmutableEndpoint endpoint = getDefaultClientEndpoint(service, message.getPayload(), false);
386         MuleEvent event = new DefaultMuleEvent(message, endpoint, session);
387 
388         if (logger.isDebugEnabled())
389         {
390             logger.debug("MuleClient dispatching event direct to: " + componentName + ". MuleEvent is: " + event);
391         }
392 
393         service.dispatchEvent(event);
394     }
395 
396     /**
397      * Sends an event request to a URL, making the result of the event trigger
398      * available as a Future result that can be accessed later by client code.
399      * 
400      * @param url the url to make a request on
401      * @param payload the object that is the payload of the event
402      * @param messageProperties any properties to be associated with the payload. as
403      *            null
404      * @return the result message if any of the invocation
405      * @throws org.mule.api.MuleException if the dispatch fails or the components or
406      *             transfromers cannot be found
407      */
408     public FutureMessageResult sendAsync(final String url, final Object payload, final Map messageProperties)
409         throws MuleException
410     {
411         return sendAsync(url, payload, messageProperties, 0);
412     }
413 
414     /**
415      * Sends an event request to a URL, making the result of the event trigger
416      * available as a Future result that can be accessed later by client code.
417      * 
418      * @param url the URL to make a request on
419      * @param message the message to send
420      * @return the result message if any of the invocation
421      * @throws org.mule.api.MuleException if the dispatch fails or the components or
422      *             transfromers cannot be found
423      */
424     public FutureMessageResult sendAsync(final String url, final MuleMessage message) throws MuleException
425     {
426         return sendAsync(url, message, MuleEvent.TIMEOUT_NOT_SET_VALUE);
427     }
428 
429     /**
430      * Sends an event request to a URL, making the result of the event trigger
431      * available as a Future result that can be accessed later by client code.
432      * 
433      * @param url the url to make a request on
434      * @param payload the object that is the payload of the event
435      * @param messageProperties any properties to be associated with the payload. as
436      *            null
437      * @param timeout how long to block in milliseconds waiting for a result
438      * @return the result message if any of the invocation
439      * @throws org.mule.api.MuleException if the dispatch fails or the components or
440      *             transfromers cannot be found
441      */
442     public FutureMessageResult sendAsync(final String url,
443                                          final Object payload,
444                                          final Map messageProperties,
445                                          final int timeout) throws MuleException
446     {
447         return sendAsync(url, new DefaultMuleMessage(payload, messageProperties, muleContext), timeout);
448     }
449 
450     /**
451      * Sends an event request to a URL, making the result of the event trigger
452      * available as a Future result that can be accessed later by client code.
453      * 
454      * @param url the url to make a request on
455      * @param message the message to send
456      * @param timeout how long to block in milliseconds waiting for a result
457      * @return the result message if any of the invocation
458      * @throws org.mule.api.MuleException if the dispatch fails or the components or
459      *             transfromers cannot be found
460      */
461     public FutureMessageResult sendAsync(final String url, final MuleMessage message, final int timeout)
462         throws MuleException
463     {
464         Callable call = new Callable()
465         {
466             public Object call() throws Exception
467             {
468                 return send(url, message, timeout);
469             }
470         };
471 
472         FutureMessageResult result = new FutureMessageResult(call, muleContext);
473 
474         if (muleContext.getWorkManager() != null)
475         {
476             result.setExecutor(muleContext.getWorkManager());
477         }
478 
479         result.execute();
480         return result;
481     }
482 
483     /**
484      * Sends an event to a component on a local Mule instance, while making the
485      * result of the event trigger available as a Future result that can be accessed
486      * later by client code. Users can specify a url to a remote Mule server in the
487      * constructor of a Mule client, by default the default Mule server url
488      * <code>tcp://localhost:60504</code> is used.
489      * 
490      * @param component the name of the Mule components to send to
491      * @param transformers a comma separated list of transformers to apply to the
492      *            result message
493      * @param payload the object that is the payload of the event
494      * @param messageProperties any properties to be associated with the payload.
495      * @return the result message if any of the invocation
496      * @throws org.mule.api.MuleException if the dispatch fails or the components or
497      *             transfromers cannot be found
498      */
499     public FutureMessageResult sendDirectAsync(final String component,
500                                                String transformers,
501                                                final Object payload,
502                                                final Map messageProperties) throws MuleException
503     {
504         return sendDirectAsync(component, transformers, new DefaultMuleMessage(payload, messageProperties, muleContext));
505     }
506 
507     /**
508      * Snds an event to a component on a local Mule instance, while making the result
509      * of the event trigger available as a Future result that can be accessed later
510      * by client code. Users can specify a url to a remote Mule server in the
511      * constructor of a Mule client, by default the default Mule server url
512      * <code>tcp://localhost:60504</code> is used.
513      * 
514      * @param component the name of the Mule components to send to
515      * @param transformers a comma separated list of transformers to apply to the
516      *            result message
517      * @param message the message to send
518      * @return the result message if any of the invocation
519      * @throws org.mule.api.MuleException if the dispatch fails or the components or
520      *             transfromers cannot be found
521      */
522     public FutureMessageResult sendDirectAsync(final String component,
523                                                String transformers,
524                                                final MuleMessage message) throws MuleException
525     {
526         Callable call = new Callable()
527         {
528             public Object call() throws Exception
529             {
530                 return sendDirect(component, null, message);
531             }
532         };
533 
534         FutureMessageResult result = new FutureMessageResult(call, muleContext);
535 
536         if (muleContext.getWorkManager() != null)
537         {
538             result.setExecutor(muleContext.getWorkManager());
539         }
540 
541         if (StringUtils.isNotBlank(transformers))
542         {
543             result.setTransformers(TransformerUtils.getTransformers(transformers, muleContext));
544         }
545 
546         result.execute();
547         return result;
548     }
549 
550     /**
551      * Sends an event synchronously to a endpointUri via a Mule server and a
552      * resulting message is returned.
553      * 
554      * @param url the Mule URL used to determine the destination and transport of the
555      *            message
556      * @param payload the object that is the payload of the event
557      * @param messageProperties any properties to be associated with the payload. In
558      *            the case of Jms you could set the JMSReplyTo property in these
559      *            properties.
560      * @return A return message, this could be <code>null</code> if the the components invoked
561      *         explicitly sets a return as <code>null</code>.
562      * @throws org.mule.api.MuleException
563      */
564     public MuleMessage send(String url, Object payload, Map messageProperties) throws MuleException
565     {
566         return send(url, payload, messageProperties, MuleEvent.TIMEOUT_NOT_SET_VALUE);
567     }
568 
569     /**
570      * Sends an event synchronously to a endpointUri via a Mule server and a
571      * resulting message is returned.
572      * 
573      * @param url the Mule URL used to determine the destination and transport of the
574      *            message
575      * @param message the Message for the event
576      * @return A return message, this could be <code>null</code> if the the components invoked
577      *         explicitly sets a return as <code>null</code>.
578      * @throws org.mule.api.MuleException
579      */
580     public MuleMessage send(String url, MuleMessage message) throws MuleException
581     {
582         return send(url, message, MuleEvent.TIMEOUT_NOT_SET_VALUE);
583     }
584 
585     /**
586      * Sends an event synchronously to a endpointUri via a mule server and a
587      * resulting message is returned.
588      * 
589      * @param url the Mule URL used to determine the destination and transport of the
590      *            message
591      * @param payload the object that is the payload of the event
592      * @param messageProperties any properties to be associated with the payload. In
593      *            the case of Jms you could set the JMSReplyTo property in these
594      *            properties.
595      * @param timeout The time in milliseconds the the call should block waiting for
596      *            a response
597      * @return A return message, this could be <code>null</code> if the the components invoked
598      *         explicitly sets a return as <code>null</code>.
599      * @throws org.mule.api.MuleException
600      */
601     public MuleMessage send(String url, Object payload, Map messageProperties, int timeout)
602         throws MuleException
603     {
604         if (messageProperties == null)
605         {
606             messageProperties = new HashMap();
607         }
608         if (messageProperties.get(MuleProperties.MULE_REMOTE_SYNC_PROPERTY) == null)
609         {
610             // clone the map in case a call used an unmodifiable version
611             messageProperties = new HashMap(messageProperties);
612             messageProperties.put(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, "true");
613         }
614         MuleMessage message = new DefaultMuleMessage(payload, messageProperties, muleContext);
615         return send(url, message, timeout);
616     }
617 
618     /**
619      * Sends an event synchronously to a endpointUri via a mule server and a
620      * resulting message is returned.
621      * 
622      * @param url the Mule URL used to determine the destination and transport of the
623      *            message
624      * @param message The message to send
625      * @param timeout The time in milliseconds the the call should block waiting for
626      *            a response
627      * @return A return message, this could be <code>null</code> if the the components invoked
628      *         explicitly sets a return as <code>null</code>.
629      * @throws org.mule.api.MuleException
630      */
631     public MuleMessage send(String url, MuleMessage message, int timeout) throws MuleException
632     {
633         OutboundEndpoint endpoint = 
634             getOutboundEndpoint(url, MessageExchangePattern.REQUEST_RESPONSE, timeout);
635         
636         MuleEvent event = getEvent(message, endpoint);
637         event.setTimeout(timeout);
638 
639         MuleEvent response = endpoint.process(event);
640         if (response != null)
641         {
642 //            if (response.getMessage().getExceptionPayload() == null)
643 //            {
644                 return response.getMessage();
645 //            }
646 //            else
647 //            {
648 //                Throwable e = response.getMessage().getExceptionPayload().getException();
649 //                if (e instanceof MuleException)
650 //                {
651 //                    throw (MuleException) e;
652 //                }
653 //                else
654 //                {
655 //                    throw new MessagingException(message, e);
656 //                }
657 //            }
658         }
659         else
660         {
661             return new DefaultMuleMessage(NullPayload.getInstance(), muleContext);
662         }
663     }
664 
665     /**
666      * Will receive an event from an endpointUri determined by the URL.
667      * 
668      * @param url the Mule URL used to determine the destination and transport of the
669      *            message
670      * @param timeout how long to block waiting to receive the event, if set to 0 the
671      *            receive will not wait at all and if set to -1 the receive will wait
672      *            forever
673      * @return the message received or <code>null</code> if no message was received
674      * @throws org.mule.api.MuleException
675      */
676     public MuleMessage request(String url, long timeout) throws MuleException
677     {
678         InboundEndpoint endpoint = getInboundEndpoint(url);
679         try
680         {
681             return endpoint.request(timeout);
682         }
683         catch (Exception e)
684         {
685             throw new ReceiveException(endpoint, timeout, e);
686         }
687     }
688 
689     /**
690      * Will receive an event from an endpointUri determined by the URL
691      * 
692      * @param url the Mule URL used to determine the destination and transport of the
693      *            message
694      * @param transformers A comma separated list of transformers used to apply to
695      *            the result message
696      * @param timeout how long to block waiting to receive the event, if set to 0 the
697      *            receive will not wait at all and if set to -1 the receive will wait
698      *            forever
699      * @return the message received or <code>null</code> if no message was received
700      * @throws org.mule.api.MuleException
701      */
702     public MuleMessage request(String url, String transformers, long timeout) throws MuleException
703     {
704         return request(url, TransformerUtils.getTransformers(transformers, muleContext), timeout);
705     }
706 
707     /**
708      * Will receive an event from an endpointUri determined by the URL
709      * 
710      * @param url the Mule URL used to determine the destination and transport of the
711      *            message
712      * @param transformers Transformers used to modify the result message
713      * @param timeout how long to block waiting to receive the event, if set to 0 the
714      *            receive will not wait at all and if set to -1 the receive will wait
715      *            forever
716      * @return the message received or <code>null</code> if no message was received
717      * @throws org.mule.api.MuleException
718      */
719     public MuleMessage request(String url, List transformers, long timeout) throws MuleException
720     {
721         return request(url, timeout);
722     }
723 
724     protected MuleEvent getEvent(MuleMessage message, OutboundEndpoint endpoint) throws MuleException
725     {
726         DefaultMuleSession session = new DefaultMuleSession(new DefaultLocalMuleClient.MuleClientFlowConstruct(muleContext), muleContext);
727 
728         if (user != null)
729         {
730             message.setOutboundProperty(MuleProperties.MULE_USER_PROPERTY, MuleCredentials.createHeader(user.getUsername(), user.getPassword()));
731         }
732         return new DefaultMuleEvent(message, endpoint, session);
733     }
734 
735     protected InboundEndpoint getInboundEndpoint(String uri) throws MuleException
736     {
737         // There was a potential leak here between get() and putIfAbsent(). This
738         // would cause the endpoint that was created to be used rather an endpoint
739         // with the same key that has been created and put in the cache by another
740         // thread. To avoid this we test for the result of putIfAbsent result and if
741         // it is non-null then an endpoint was created and added concurrently and we
742         // return this instance instead.
743         InboundEndpoint endpoint = (InboundEndpoint) inboundEndpointCache.get(uri);
744         if (endpoint == null)
745         {
746             endpoint = muleContext.getEndpointFactory().getInboundEndpoint(uri);
747             InboundEndpoint concurrentlyAddedEndpoint = (InboundEndpoint) inboundEndpointCache.putIfAbsent(uri, endpoint);
748             if (concurrentlyAddedEndpoint != null)
749             {
750                 return concurrentlyAddedEndpoint;
751             }
752         }
753         return endpoint;
754     }
755 
756     protected OutboundEndpoint getOutboundEndpoint(String uri, MessageExchangePattern exchangePattern, 
757         Integer responseTimeout) throws MuleException
758     {
759         // There was a potential leak here between get() and putIfAbsent(). This
760         // would cause the endpoint that was created to be used rather an endpoint
761         // with the same key that has been created and put in the cache by another
762         // thread. To avoid this we test for the result of putIfAbsent result and if
763         // it is non-null then an endpoint was created and added concurrently and we
764         // return this instance instead.
765         String key = String.format("%1s:%2s:%3s", uri, exchangePattern, responseTimeout);
766         OutboundEndpoint endpoint = (OutboundEndpoint) outboundEndpointCache.get(key);
767         if (endpoint == null)
768         {
769             EndpointBuilder endpointBuilder = 
770                 muleContext.getEndpointFactory().getEndpointBuilder(uri);
771             endpointBuilder.setExchangePattern(exchangePattern);
772             if (responseTimeout != null && responseTimeout > 0)
773             {
774                 endpointBuilder.setResponseTimeout(responseTimeout.intValue());
775             }
776             endpoint = muleContext.getEndpointFactory().getOutboundEndpoint(endpointBuilder);
777             OutboundEndpoint concurrentlyAddedEndpoint = 
778                 (OutboundEndpoint) outboundEndpointCache.putIfAbsent(key, endpoint);
779             if (concurrentlyAddedEndpoint != null)
780             {
781                 return concurrentlyAddedEndpoint;
782             }
783         }
784         return endpoint;
785     }
786 
787     protected ImmutableEndpoint getDefaultClientEndpoint(Service service, Object payload, boolean sync)
788         throws MuleException
789     {
790         if (!(service.getMessageSource() instanceof ServiceCompositeMessageSource))
791         {
792             throw new IllegalStateException(
793                 "Only 'CompositeMessageSource' is supported with MuleClient.sendDirect() and MuleClient.dispatchDirect()");
794         }
795     
796         // as we are bypassing the message transport layer we need to check that
797         ImmutableEndpoint endpoint = (ImmutableEndpoint) ((ServiceCompositeMessageSource) service.getMessageSource()).getEndpoints().get(0);
798         if (endpoint != null)
799         {
800             if (endpoint.getTransformers() != null)
801             {
802                 // the original code here really did just check the first exception
803                 // as far as i can tell
804                 if (TransformerUtils.isSourceTypeSupportedByFirst(endpoint.getTransformers(),
805                     payload.getClass()))
806                 {
807                     return endpoint;
808                 }
809                 else
810                 {
811                     EndpointBuilder builder = new EndpointURIEndpointBuilder(endpoint);
812                     builder.setTransformers(new LinkedList());
813                     builder.setExchangePattern(MessageExchangePattern.REQUEST_RESPONSE);
814                     return muleContext.getEndpointFactory().getInboundEndpoint(builder);
815                 }
816             }
817             else
818             {
819                 return endpoint;
820             }
821         }
822         else
823         {
824             EndpointBuilder builder = new EndpointURIEndpointBuilder("vm://mule.client", muleContext);
825             builder.setName("muleClientProvider");
826             endpoint = muleContext.getEndpointFactory().getInboundEndpoint(builder);
827         }
828         return endpoint;
829     }
830 
831     /**
832      * Sends an event synchronously to a endpointUri via a Mule server without
833      * waiting for the result.
834      * 
835      * @param url the Mule URL used to determine the destination and transport of the
836      *            message
837      * @param payload the object that is the payload of the event
838      * @param messageProperties any properties to be associated with the payload. In
839      *            the case of Jms you could set the JMSReplyTo property in these
840      *            properties.
841      * @throws org.mule.api.MuleException
842      */
843     public void sendNoReceive(String url, Object payload, Map<String, Object> messageProperties) throws MuleException
844     {
845         if (messageProperties == null)
846         {
847             messageProperties = new HashMap<String, Object>();
848         }
849         messageProperties.put(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, "false");
850         MuleMessage message = new DefaultMuleMessage(payload, messageProperties, muleContext);
851         
852         OutboundEndpoint endpoint = 
853             getOutboundEndpoint(url, MessageExchangePattern.REQUEST_RESPONSE, null);
854         MuleEvent event = getEvent(message, endpoint);
855         try
856         {
857             endpoint.process(event);
858         }
859         catch (MuleException e)
860         {
861             throw e;
862         }
863         catch (Exception e)
864         {
865             throw new DispatchException(ClientMessages.failedToDispatchClientEvent(), event,
866                 endpoint, e);
867         }
868     }
869 
870     /**
871      * The overriding method may want to return a custom {@link MuleContext} here
872      * 
873      * @return the MuleContext to use
874      */
875     public MuleContext getMuleContext()
876     {
877         return muleContext;
878     }
879 
880     /**
881      * Registers a Java object as a component that listens for events on the
882      * given URL. By default the ThreadingProfile for the components will be set so
883      * that there will only be one thread of execution.
884      * 
885      * @param component any java object, Mule will it's endpointUri discovery to
886      *            determine which event to invoke based on the evnet payload type
887      * @param name The identifying name of the components. This can be used to later
888      *            unregister it
889      * @param listenerEndpoint The url endpointUri to listen to
890      * @throws MuleException
891      * @deprecated Use the RegistryContext to get the registry and register the
892      *             service there
893      */
894     @Deprecated
895     public void registerComponent(Object component, String name, EndpointURI listenerEndpoint)
896         throws MuleException
897     {
898         throw new UnsupportedOperationException("registerComponent");
899         // builder.registerComponentInstance(service, name, listenerEndpoint,
900         // null);
901     }
902 
903     /**
904      * Registers a Java object as a component that listens for and sends events
905      * on the given urls. By default the ThreadingProfile for the components will be
906      * set so that there will only be one thread of execution.
907      * 
908      * @param component any java object, Mule will it's endpointUri discovery to
909      *            determine which event to invoke based on the evnet payload type
910      * @param name The identifying name of the components. This can be used to later
911      *            unregister it
912      * @param listenerEndpoint The url endpointUri to listen to
913      * @param sendEndpoint The url endpointUri to dispatch to
914      * @throws MuleException
915      * @deprecated Use the RegistryContext to get the registry and register the
916      *             service there
917      */
918     @Deprecated
919     public void registerComponent(Object component,
920                                   String name,
921                                   MuleEndpointURI listenerEndpoint,
922                                   MuleEndpointURI sendEndpoint) throws MuleException
923     {
924         throw new UnsupportedOperationException("registerComponent");
925         // builder.registerComponentInstance(service, name, listenerEndpoint,
926         // sendEndpoint);
927     }
928 
929     /**
930      * Registers a user configured MuleDescriptor of a components to the server. If
931      * users want to register object instances with the server rather than class
932      * names that get created at runtime or reference to objects in the container,
933      * the user must call the descriptors setImplementationInstance() method - <code>
934      * MyBean implementation = new MyBean();
935      * descriptor.setImplementationInstance(implementation);
936      * </code>
937      * Calling this method is equivilent to calling Model.registerComponent(..)
938      * 
939      * @param descriptor the componet descriptor to register
940      * @throws MuleException the descriptor is invalid or cannot be initialised or
941      *             started
942      * @see org.mule.api.model.Model
943      * @deprecated Use the RegistryContext to get the registry and register the
944      *             service there
945      */
946     // public void registerComponent(UMODescriptor descriptor) throws MuleException
947     // {
948     // throw new UnsupportedOperationException("registerComponent");
949     // //builder.registerComponent(descriptor);
950     // }
951     /**
952      * Unregisters a previously register components. This will also unregister any
953      * listeners for the components Calling this method is equivilent to calling
954      * Model.unregisterComponent(..)
955      * 
956      * @param name the name of the componet to unregister
957      * @throws MuleException if unregistering the components fails, i.e. The
958      *             underlying transport fails to unregister a listener. If the
959      *             components does not exist, this method should not throw an
960      *             exception.
961      * @see org.mule.api.model.Model
962      * @deprecated Use the RegistryContext to get the registry and unregister the
963      *             service there
964      */
965     @Deprecated
966     public void unregisterComponent(String name) throws MuleException
967     {
968         throw new UnsupportedOperationException("registerComponent");
969 
970         // builder.unregisterComponent(name);
971     }
972 
973     public RemoteDispatcher getRemoteDispatcher(String serverEndpoint) throws MuleException
974     {
975         RemoteDispatcher rd = new RemoteDispatcher(serverEndpoint, muleContext);
976         rd.setExecutor(muleContext.getWorkManager());
977         dispatchers.add(rd);
978         return rd;
979     }
980 
981     public RemoteDispatcher getRemoteDispatcher(String serverEndpoint, String user, String password)
982         throws MuleException
983     {
984         RemoteDispatcher rd = new RemoteDispatcher(serverEndpoint, new MuleCredentials(user,
985             password.toCharArray()), muleContext);
986         rd.setExecutor(muleContext.getWorkManager());
987         dispatchers.add(rd);
988         return rd;
989     }
990 
991     /**
992      * Will dispose the MuleManager instance <b>if</b> a new instance was created for this
993      * client. Otherwise this method only cleans up resources no longer needed
994      */
995     public void dispose()
996     {
997         synchronized (dispatchers)
998         {
999             for (Iterator iterator = dispatchers.iterator(); iterator.hasNext();)
1000             {
1001                 RemoteDispatcher remoteDispatcher = (RemoteDispatcher) iterator.next();
1002                 remoteDispatcher.dispose();
1003                 remoteDispatcher = null;
1004             }
1005             dispatchers.clear();
1006         }
1007         // Dispose the muleContext only if the muleContext was created for this
1008         // client
1009         if (muleContext.getConfiguration().isClientMode())
1010         {
1011             logger.info("Stopping Mule...");
1012             muleContext.dispose();
1013         }
1014     }
1015 
1016     public void setProperty(String key, Object value)
1017     {
1018         try
1019         {
1020             muleContext.getRegistry().registerObject(key, value);
1021         }
1022         catch (RegistrationException e)
1023         {
1024             logger.error(e);
1025         }
1026     }
1027 
1028     public Object getProperty(String key)
1029     {
1030         return muleContext.getRegistry().lookupObject(key);
1031     }
1032 
1033     public MuleConfiguration getConfiguration()
1034     {
1035         return muleContext.getConfiguration();
1036     }    
1037 }