View Javadoc

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