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.transport;
8   
9   import org.mule.MessageExchangePattern;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleEvent;
12  import org.mule.api.MuleException;
13  import org.mule.api.MuleMessage;
14  import org.mule.api.MuleRuntimeException;
15  import org.mule.api.config.MuleProperties;
16  import org.mule.api.config.ThreadingProfile;
17  import org.mule.api.construct.FlowConstruct;
18  import org.mule.api.context.WorkManager;
19  import org.mule.api.context.WorkManagerSource;
20  import org.mule.api.context.notification.ServerNotification;
21  import org.mule.api.context.notification.ServerNotificationHandler;
22  import org.mule.api.endpoint.EndpointURI;
23  import org.mule.api.endpoint.ImmutableEndpoint;
24  import org.mule.api.endpoint.InboundEndpoint;
25  import org.mule.api.endpoint.OutboundEndpoint;
26  import org.mule.api.lifecycle.CreateException;
27  import org.mule.api.lifecycle.InitialisationException;
28  import org.mule.api.lifecycle.LifecycleCallback;
29  import org.mule.api.lifecycle.LifecycleException;
30  import org.mule.api.lifecycle.LifecycleState;
31  import org.mule.api.processor.MessageProcessor;
32  import org.mule.api.registry.ServiceException;
33  import org.mule.api.registry.ServiceType;
34  import org.mule.api.retry.RetryCallback;
35  import org.mule.api.retry.RetryContext;
36  import org.mule.api.retry.RetryPolicyTemplate;
37  import org.mule.api.transformer.Transformer;
38  import org.mule.api.transport.Connectable;
39  import org.mule.api.transport.Connector;
40  import org.mule.api.transport.ConnectorException;
41  import org.mule.api.transport.DispatchException;
42  import org.mule.api.transport.MessageDispatcher;
43  import org.mule.api.transport.MessageDispatcherFactory;
44  import org.mule.api.transport.MessageReceiver;
45  import org.mule.api.transport.MessageRequester;
46  import org.mule.api.transport.MessageRequesterFactory;
47  import org.mule.api.transport.MuleMessageFactory;
48  import org.mule.api.transport.ReplyToHandler;
49  import org.mule.api.transport.SessionHandler;
50  import org.mule.config.i18n.CoreMessages;
51  import org.mule.config.i18n.MessageFactory;
52  import org.mule.context.notification.ConnectionNotification;
53  import org.mule.context.notification.EndpointMessageNotification;
54  import org.mule.context.notification.OptimisedNotificationHandler;
55  import org.mule.endpoint.outbound.OutboundNotificationMessageProcessor;
56  import org.mule.model.streaming.DelegatingInputStream;
57  import org.mule.processor.OptionalAsyncInterceptingMessageProcessor;
58  import org.mule.processor.chain.DefaultMessageProcessorChainBuilder;
59  import org.mule.routing.filters.WildcardFilter;
60  import org.mule.session.SerializeAndEncodeSessionHandler;
61  import org.mule.transformer.TransformerUtils;
62  import org.mule.transport.service.TransportFactory;
63  import org.mule.transport.service.TransportServiceDescriptor;
64  import org.mule.transport.service.TransportServiceException;
65  import org.mule.util.ClassUtils;
66  import org.mule.util.CollectionUtils;
67  import org.mule.util.ObjectNameHelper;
68  import org.mule.util.ObjectUtils;
69  import org.mule.util.StringUtils;
70  import org.mule.util.concurrent.NamedThreadFactory;
71  import org.mule.util.concurrent.ThreadNameHelper;
72  
73  import java.io.IOException;
74  import java.io.InputStream;
75  import java.io.OutputStream;
76  import java.text.MessageFormat;
77  import java.util.ArrayList;
78  import java.util.Collections;
79  import java.util.Iterator;
80  import java.util.List;
81  import java.util.Map;
82  import java.util.Properties;
83  
84  import javax.resource.spi.work.WorkEvent;
85  import javax.resource.spi.work.WorkListener;
86  
87  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
88  import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService;
89  import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor;
90  import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory;
91  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
92  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
93  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicReference;
94  
95  import org.apache.commons.logging.Log;
96  import org.apache.commons.logging.LogFactory;
97  import org.apache.commons.pool.KeyedPoolableObjectFactory;
98  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
99  
100 /**
101  * <code>AbstractConnector</code> provides base functionality for all connectors
102  * provided with Mule. Connectors are the mechanism used to connect to external
103  * systems and protocols in order to send and receive data.
104  * <p/>
105  * The <code>AbstractConnector</code> provides getter and setter methods for endpoint
106  * name, transport name and protocol. It also provides methods to stop and start
107  * connectors and sets up a dispatcher threadpool which allows deriving connectors
108  * the possibility to dispatch work to separate threads. This functionality is
109  * controlled with the <i> doThreading</i> property on the threadingProfiles for
110  * dispatchers and receivers. The lifecycle for a connector is -
111  * <ol>
112  * <li>Create
113  * <li>Initialise
114  * <li>Connect
115  * <li>Connect receivers
116  * <li>Start
117  * <li>Start Receivers
118  * <li>Stop
119  * <li>Stop Receivers
120  * <li>Disconnect
121  * <li>Disconnect Receivers
122  * <li>Dispose
123  * <li>Dispose Receivers
124  * </ol>
125  */
126 public abstract class AbstractConnector implements Connector, WorkListener
127 {
128     /**
129      * Default number of concurrent transactional receivers.
130      */
131     public static final int DEFAULT_NUM_CONCURRENT_TX_RECEIVERS = 4;
132 
133     private static final long SCHEDULER_FORCED_SHUTDOWN_TIMEOUT = 5000l;
134     
135     public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
136 
137     /**
138      * logger used by this class
139      */
140     protected final Log logger = LogFactory.getLog(getClass());
141 
142     /**
143      * The name that identifies the endpoint
144      */
145     protected volatile String name;
146 
147     /**
148      * Factory used to create dispatchers for this connector
149      */
150     protected volatile MessageDispatcherFactory dispatcherFactory;
151 
152     /**
153      * Factory used to create requesters for this connector
154      */
155     protected volatile MessageRequesterFactory requesterFactory;
156 
157     /**
158      * Factory used to create new {@link MuleMessage} instances
159      */
160     protected MuleMessageFactory muleMessageFactory;
161 
162     /**
163      * A pool of dispatchers for this connector, keyed by endpoint
164      */
165     protected volatile ConfigurableKeyedObjectPool dispatchers;
166 
167     /**
168      * A factory for creating the pool of dispatchers for this connector.
169      */
170     protected volatile ConfigurableKeyedObjectPoolFactory dispatcherPoolFactory;
171 
172     /**
173      * A pool of requesters for this connector, keyed by endpoint
174      */
175     protected final GenericKeyedObjectPool requesters = new GenericKeyedObjectPool();
176 
177     /**
178      * The collection of listeners on this connector. Keyed by entrypoint
179      */
180     @SuppressWarnings("unchecked")
181     protected final Map<Object, MessageReceiver> receivers = new ConcurrentHashMap/* <Object, MessageReceiver> */();
182 
183     /**
184      * Defines the dispatcher threading profile
185      */
186     private volatile ThreadingProfile dispatcherThreadingProfile;
187 
188     /**
189      * Defines the requester threading profile
190      */
191     private volatile ThreadingProfile requesterThreadingProfile;
192 
193     /**
194      * Defines the receiver threading profile
195      */
196     private volatile ThreadingProfile receiverThreadingProfile;
197 
198     /**
199      * @see #isCreateMultipleTransactedReceivers()
200      */
201     protected volatile boolean createMultipleTransactedReceivers = true;
202 
203     /**
204      * @see #getNumberOfConcurrentTransactedReceivers()
205      */
206     protected volatile int numberOfConcurrentTransactedReceivers = DEFAULT_NUM_CONCURRENT_TX_RECEIVERS;
207 
208     private RetryPolicyTemplate retryPolicyTemplate;
209 
210     /**
211      * Optimise the handling of message notifications. If dynamic is set to false
212      * then the cached notification handler implements a shortcut for message
213      * notifications.
214      */
215     private boolean dynamicNotification = false;
216     private ServerNotificationHandler cachedNotificationHandler;
217 
218     private final List<String> supportedProtocols;
219 
220     /**
221      * A shared work manager for all receivers registered with this connector.
222      */
223     private final AtomicReference/* <WorkManager> */receiverWorkManager = new AtomicReference();
224 
225     /**
226      * A shared work manager for all requesters created for this connector.
227      */
228     private final AtomicReference/* <WorkManager> */dispatcherWorkManager = new AtomicReference();
229 
230     /**
231      * A shared work manager for all requesters created for this connector.
232      */
233     private final AtomicReference/* <WorkManager> */requesterWorkManager = new AtomicReference();
234 
235     /**
236      * A generic scheduling service for tasks that need to be performed periodically.
237      */
238     private ScheduledExecutorService scheduler;
239 
240     /**
241      * Holds the service configuration for this connector
242      */
243     protected volatile TransportServiceDescriptor serviceDescriptor;
244 
245     /**
246      * The map of service overrides that can be used to extend the capabilities of
247      * the connector
248      */
249     protected volatile Properties serviceOverrides;
250 
251     /**
252      * The strategy used for reading and writing session information to and from the
253      * transport
254      */
255     protected volatile SessionHandler sessionHandler = new SerializeAndEncodeSessionHandler();
256 
257     protected MuleContext muleContext;
258 
259     protected ConnectorLifecycleManager lifecycleManager;
260 
261     // TODO connect and disconnect are not part of lifecycle management right now
262     private AtomicBoolean connected = new AtomicBoolean(false);
263 
264     /** Is this connector currently undergoing a reconnection strategy? */
265     private AtomicBoolean reconnecting = new AtomicBoolean(false);
266 
267     /**
268      * Indicates whether the connector should start upon connecting. This is
269      * necessary to support asynchronous retry policies, otherwise the start() method
270      * would block until connection is successful.
271      */
272     protected boolean startOnConnect = false;
273 
274     /**
275      * The will cause the connector not to start when {@link #start()} is called. The
276      * only way to start the connector is to call
277      * {@link #setInitialStateStopped(boolean)} with 'false' and then calling
278      * {@link #start()}. This flag is used internally since some connectors that rely
279      * on external servers may need to wait for that server to become available
280      * before starting
281      */
282     protected boolean initialStateStopped = false;
283     /**
284      * Whether to test a connection on each take.
285      */
286     private boolean validateConnections = true;
287 
288     public AbstractConnector(MuleContext context)
289     {
290         muleContext = context;
291         lifecycleManager = new ConnectorLifecycleManager(this);
292 
293         setDynamicNotification(false);
294         updateCachedNotificationHandler();
295 
296         // always add at least the default protocol
297         supportedProtocols = new ArrayList<String>();
298         supportedProtocols.add(getProtocol().toLowerCase());
299 
300         // TODO dispatcher pool configuration should be extracted, maybe even
301         // moved into the factory?
302         // NOTE: testOnBorrow MUST be FALSE. this is a bit of a design bug in
303         // commons-pool since validate is used for both activation and passivation,
304         // but has no way of knowing which way it is going.
305         requesters.setTestOnBorrow(false);
306         requesters.setTestOnReturn(true);
307     }
308 
309     public String getName()
310     {
311         return name;
312     }
313 
314     public void setName(String newName)
315     {
316         if (newName == null)
317         {
318             throw new IllegalArgumentException(CoreMessages.objectIsNull("Connector name").toString());
319         }
320 
321         if (logger.isDebugEnabled())
322         {
323             logger.debug("Set Connector name to: " + newName);
324         }
325 
326         name = newName;
327     }
328 
329     //-----------------------------------------------------------------------------------------------//
330     //-             LIFECYCLE METHODS
331     //-----------------------------------------------------------------------------------------------//
332 
333     ConnectorLifecycleManager getLifecycleManager()
334     {
335         return lifecycleManager;
336     }
337 
338     public LifecycleState getLifecycleState()
339     {
340         return lifecycleManager.getState();
341     }
342 
343     public final synchronized void initialise() throws InitialisationException
344     {
345         try
346         {
347             lifecycleManager.fireInitialisePhase(new LifecycleCallback<Connector>()
348             {
349                 public void onTransition(String phaseName, Connector object) throws MuleException
350                 {
351                     if (retryPolicyTemplate == null)
352                     {
353                         retryPolicyTemplate = (RetryPolicyTemplate) muleContext.getRegistry().lookupObject(
354                                 MuleProperties.OBJECT_DEFAULT_RETRY_POLICY_TEMPLATE);
355                     }
356 
357                     if (dispatcherPoolFactory == null) {
358                         dispatcherPoolFactory = new DefaultConfigurableKeyedObjectPoolFactory();
359                     }
360 
361                     dispatchers = dispatcherPoolFactory.createObjectPool();
362                     if (dispatcherFactory != null) {
363                         dispatchers.setFactory(getWrappedDispatcherFactory(dispatcherFactory));
364                     }
365 
366                     // Initialise the structure of this connector
367                     initFromServiceDescriptor();
368 
369                     configureDispatcherPool();
370                     setMaxRequestersActive(getRequesterThreadingProfile().getMaxThreadsActive());
371 
372                     doInitialise();
373 
374                     try
375                     {
376                         initWorkManagers();
377                     }
378                     catch (MuleException e)
379                     {
380                         throw new LifecycleException(e, this);
381                     }
382                 }
383             });
384         }
385         catch (InitialisationException e)
386         {
387             throw e;
388         }
389         catch (LifecycleException e)
390         {
391             throw new InitialisationException(e, this);
392         }
393         catch (MuleException e)
394         {
395             e.printStackTrace();
396         }
397     }
398 
399     // Start (but we might not be connected yet).
400     public final synchronized void start() throws MuleException
401     {
402         if (isInitialStateStopped())
403         {
404             logger.info("Connector not started because 'initialStateStopped' is true");
405             return;
406         }
407 
408         if (!isConnected())
409         {
410             try
411             {
412                 // startAfterConnect() will get called from the connect() method once connected.
413                 // This is necessary for reconnection strategies.
414                 startOnConnect = true;
415                 connect();
416             }
417             catch (MuleException me)
418             {
419                 throw me;
420             }
421             catch (Exception e)
422             {
423                 throw new ConnectException(e, this);
424             }
425         }
426         else
427         {        
428             startAfterConnect();
429         }
430     }
431 
432     // Start now that we're sure we're connected.
433     protected synchronized void startAfterConnect() throws MuleException
434     {
435         // Reset this flag if it was set
436         startOnConnect = false;
437         
438         // This breaks ConnectorLifecycleTestCase.testDoubleStartConnector()
439         //if (isStarted())
440         //{
441         //    return;
442         //}
443         
444         if (logger.isInfoEnabled())
445         {
446             logger.info("Starting: " + this);
447         }
448 
449         lifecycleManager.fireStartPhase(new LifecycleCallback<Connector>()
450         {
451             public void onTransition(String phaseName, Connector object) throws MuleException
452             {
453                 initWorkManagers();        
454                 scheduler = createScheduler();
455                 doStart();
456                 
457                 if (receivers != null)
458                 {
459                     for (MessageReceiver receiver : receivers.values())
460                     {
461                         final List<MuleException> errors = new ArrayList<MuleException>();
462                         try
463                         {
464                             if (logger.isDebugEnabled())
465                             {
466                                 logger.debug("Starting receiver on endpoint: "
467                                         + receiver.getEndpoint().getEndpointURI());
468                             }
469                             if (receiver.getFlowConstruct().getLifecycleState().isStarted())
470                             {
471                                 receiver.start();
472                             }
473                         }
474                         catch (MuleException e)
475                         {
476                             logger.error(e);
477                             errors.add(e);
478                         }
479 
480                         if (!errors.isEmpty())
481                         {
482                             // throw the first one in order not to break the reconnection
483                             // strategy logic,
484                             // every exception has been logged above already
485                             // api needs refactoring to support the multi-cause exception
486                             // here
487                             throw errors.get(0);
488                         }
489                     }
490                 }                
491             }
492         });
493     }
494 
495     public final synchronized void stop() throws MuleException
496     {
497         // This breaks ConnectorLifecycleTestCase.testDoubleStopConnector()
498         //if (isStopped() || isStopping())
499         //{
500         //    return;
501         //}
502         
503         lifecycleManager.fireStopPhase(new LifecycleCallback<Connector>()
504         {
505             public void onTransition(String phaseName, Connector object) throws MuleException
506             {
507                  // shutdown our scheduler service
508                 shutdownScheduler();
509 
510                 doStop();
511 
512                 // Stop all the receivers on this connector
513                 if (receivers != null)
514                 {
515                     for (MessageReceiver receiver : receivers.values())
516                     {
517                         if (logger.isDebugEnabled())
518                         {
519                             logger.debug("Stopping receiver on endpoint: " + receiver.getEndpoint().getEndpointURI());
520                         }
521                         receiver.stop();
522                     }
523                 }
524 
525                 // TODO We shouldn't need to automatically disconnect just because we're stopping, these are 
526                 // discrete stages in the connector's lifecycle.  
527                 if (isConnected())
528                 {
529                     try
530                     {
531                         disconnect();
532                     }
533                     catch (Exception e)
534                     {
535                         //TODO We only log here since we need to make sure we stop with
536                         //a consistent state. Another option would be to collect exceptions
537                         //and handle them at the end of this message
538                         logger.error("Failed to disconnect: " + e.getMessage(), e);
539                     }
540                 }
541 
542                 // Now that dispatchers are borrowed/returned in worker thread we need to
543                 // dispose workManager before clearing object pools
544                 disposeWorkManagers();
545 
546                 // Workaround for MULE-4553
547                 clearDispatchers();
548                 clearRequesters();
549 
550                 // make sure the scheduler is gone
551                 scheduler = null;
552             }
553         });
554     }
555 
556     public final synchronized void dispose()
557     {
558         try
559         {
560             if (isStarted())
561             {
562                 stop();
563             }
564             if (isConnected())
565             {
566                 disconnect();
567             }
568         }
569         catch (Exception e)
570         {
571             logger.warn(e.getMessage(), e);
572         }
573         
574         try
575         {
576             lifecycleManager.fireDisposePhase(new LifecycleCallback<Connector>()
577             {
578                 public void onTransition(String phaseName, Connector object) throws MuleException
579                 {
580                     doDispose();
581                     disposeReceivers();
582                 }
583             });
584         }
585         catch (MuleException e)
586         {
587             logger.warn(e.getMessage(), e);
588         }
589     }
590 
591     public final boolean isStarted()
592     {
593         return lifecycleManager.getState().isStarted();
594     }
595 
596     public final boolean isStarting()
597     {
598         return lifecycleManager.getState().isStarting();
599     }
600 
601     public boolean isInitialised()
602     {
603         return lifecycleManager.getState().isInitialised();
604     }
605 
606     public boolean isStopped()
607     {
608         return lifecycleManager.getState().isStopped();
609     }
610 
611     public boolean isStopping()
612     {
613         return lifecycleManager.getState().isStopping();
614     }
615 
616 
617     //-----------------------------------------------------------------------------------------------//
618     //-             END LIFECYCLE METHODS
619     //-----------------------------------------------------------------------------------------------//
620 
621    protected void configureDispatcherPool()
622    {
623        // Normally having a the same maximum number of dispatcher objects as threads
624        // is ok.
625        int maxDispatchersActive = getDispatcherThreadingProfile().getMaxThreadsActive();
626 
627        // BUT if the WHEN_EXHAUSTED_RUN threading profile exhausted action is
628        // configured then a single
629        // additional dispatcher is required that can be used by the caller thread
630        // when it executes job's itself.
631        // Also See: MULE-4752
632        if (ThreadingProfile.WHEN_EXHAUSTED_RUN == getDispatcherThreadingProfile().getPoolExhaustedAction())
633        {
634            maxDispatchersActive++;
635        }
636        setMaxDispatchersActive(maxDispatchersActive);
637    }
638 
639     /**
640      * <p>
641      * Create a {@link MuleMessageFactory} from this connector's configuration,
642      * typically through the transport descriptor.
643      * </p>
644      * <p/>
645      * <b>Attention!</b> This method is not meant to be used by client code directly.
646      * It is only publicly available to service message receivers which should be
647      * used as <em>real</em> factories to create {@link MuleMessage} instances.
648      *
649      * @see MessageReceiver#createMuleMessage(Object)
650      * @see MessageReceiver#createMuleMessage(Object, String)
651      */
652     public MuleMessageFactory createMuleMessageFactory() throws CreateException
653     {
654         try
655         {
656             return serviceDescriptor.createMuleMessageFactory();
657         }
658         catch (TransportServiceException tse)
659         {
660             throw new CreateException(CoreMessages.failedToCreate("MuleMessageFactory"), tse, this);
661         }
662     }
663 
664     protected void shutdownScheduler()
665     {
666         if (scheduler != null)
667         {
668             // Disable new tasks from being submitted
669             scheduler.shutdown();
670             try
671             {
672                 // Wait a while for existing tasks to terminate
673                 if (!scheduler.awaitTermination(muleContext.getConfiguration().getShutdownTimeout(),
674                         TimeUnit.MILLISECONDS))
675                 {
676                     // Cancel currently executing tasks and return list of pending
677                     // tasks
678                     List outstanding = scheduler.shutdownNow();
679                     // Wait a while for tasks to respond to being cancelled
680                     if (!scheduler.awaitTermination(SCHEDULER_FORCED_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS))
681                     {
682                         logger.warn(MessageFormat.format(
683                                 "Pool {0} did not terminate in time; {1} work items were cancelled.", name,
684                                 outstanding.isEmpty() ? "No" : Integer.toString(outstanding.size())));
685                     }
686                     else
687                     {
688                         if (!outstanding.isEmpty())
689                         {
690                             logger.warn(MessageFormat.format(
691                                     "Pool {0} terminated; {1} work items were cancelled.", name,
692                                     Integer.toString(outstanding.size())));
693                         }
694                     }
695 
696                 }
697             }
698             catch (InterruptedException ie)
699             {
700                 // (Re-)Cancel if current thread also interrupted
701                 scheduler.shutdownNow();
702                 // Preserve interrupt status
703                 Thread.currentThread().interrupt();
704             }
705             finally
706             {
707                 scheduler = null;
708             }
709         }
710     }
711 
712     protected void initWorkManagers() throws MuleException
713     {
714         if (receiverWorkManager.get() == null)
715         {
716 
717             final String threadPrefix = ThreadNameHelper.receiver(muleContext, getName());
718             WorkManager newWorkManager = this.getReceiverThreadingProfile().createWorkManager(
719                     threadPrefix, muleContext.getConfiguration().getShutdownTimeout());
720 
721             if (receiverWorkManager.compareAndSet(null, newWorkManager))
722             {
723                 newWorkManager.start();
724             }
725         }
726         if (dispatcherWorkManager.get() == null)
727         {
728             ThreadingProfile dispatcherThreadingProfile = this.getDispatcherThreadingProfile();
729             if (dispatcherThreadingProfile.getMuleContext() == null)
730             {
731                 dispatcherThreadingProfile.setMuleContext(muleContext);
732             }
733 
734             final String threadPrefix = ThreadNameHelper.dispatcher(muleContext, getName());
735             WorkManager newWorkManager = dispatcherThreadingProfile.createWorkManager(
736                     threadPrefix, muleContext.getConfiguration().getShutdownTimeout());
737 
738             if (dispatcherWorkManager.compareAndSet(null, newWorkManager))
739             {
740                 newWorkManager.start();
741             }
742         }
743         if (requesterWorkManager.get() == null)
744         {
745             final String threadPrefix = ThreadNameHelper.requester(muleContext, getName());
746             WorkManager newWorkManager = this.getRequesterThreadingProfile().createWorkManager(
747                     threadPrefix, muleContext.getConfiguration().getShutdownTimeout());
748 
749             if (requesterWorkManager.compareAndSet(null, newWorkManager))
750             {
751                 newWorkManager.start();
752             }
753         }
754     }
755 
756     protected void disposeWorkManagers()
757     {
758         WorkManager workManager;
759 
760         logger.debug("Disposing dispatcher work manager");
761         workManager = (WorkManager) dispatcherWorkManager.get();
762         if (workManager != null)
763         {
764             workManager.dispose();
765         }
766         dispatcherWorkManager.set(null);
767 
768         logger.debug("Disposing requester work manager");
769         workManager = (WorkManager) requesterWorkManager.get();
770         if (workManager != null)
771         {
772             workManager.dispose();
773         }
774         requesterWorkManager.set(null);
775 
776         logger.debug("Disposing receiver work manager");
777         workManager = (WorkManager) receiverWorkManager.get();
778         if (workManager != null)
779         {
780             workManager.dispose();
781         }
782         receiverWorkManager.set(null);
783     }
784 
785     protected void disposeReceivers()
786     {
787         if (receivers != null)
788         {
789             logger.debug("Disposing Receivers");
790 
791             for (MessageReceiver receiver : receivers.values())
792             {
793                 try
794                 {
795                     this.destroyReceiver(receiver, receiver.getEndpoint());
796                 }
797                 catch (Throwable e)
798                 {
799                     // Just log when disposing
800                     logger.error("Failed to destroy receiver: " + receiver, e);
801                 }
802             }
803 
804             receivers.clear();
805             logger.debug("Receivers Disposed");
806         }
807     }
808 
809     protected void clearDispatchers()
810     {
811         if (dispatchers != null)
812         {
813             logger.debug("Clearing Dispatcher pool");
814             synchronized (dispatchers)
815             {
816                 dispatchers.clear();
817             }
818             logger.debug("Dispatcher pool cleared");
819         }
820     }
821 
822     protected void clearRequesters()
823     {
824         if (requesters != null)
825         {
826             logger.debug("Clearing Requester pool");
827             requesters.clear();
828             logger.debug("Requester pool cleared");
829         }
830     }
831 
832     public boolean isDisposed()
833     {
834         return lifecycleManager.getState().isDisposed();
835     }
836 
837     public void handleException(Exception exception)
838     {
839         muleContext.getExceptionListener().handleException(exception);
840     }
841 
842     public void exceptionThrown(Exception e)
843     {
844         handleException(e);
845     }
846 
847     /**
848      * @return Returns the dispatcherFactory.
849      */
850     public MessageDispatcherFactory getDispatcherFactory()
851     {
852         return dispatcherFactory;
853     }
854 
855     /**
856      * @param dispatcherFactory The dispatcherFactory to set.
857      */
858     public void setDispatcherFactory(MessageDispatcherFactory dispatcherFactory)
859     {
860         KeyedPoolableObjectFactory poolFactory = getWrappedDispatcherFactory(dispatcherFactory);
861 
862         if (dispatchers !=  null) {
863             this.dispatchers.setFactory(poolFactory);
864         }
865 
866         // we keep a reference to the unadapted factory, otherwise people might end
867         // up with ClassCastExceptions on downcast to their implementation (sigh)
868         this.dispatcherFactory = dispatcherFactory;
869     }
870 
871     private KeyedPoolableObjectFactory getWrappedDispatcherFactory(MessageDispatcherFactory dispatcherFactory)
872     {
873         KeyedPoolableObjectFactory poolFactory;
874         if (dispatcherFactory instanceof KeyedPoolableObjectFactory)
875         {
876             poolFactory = (KeyedPoolableObjectFactory) dispatcherFactory;
877         }
878         else
879         {
880             // need to adapt the factory for use by commons-pool
881             poolFactory = new KeyedPoolMessageDispatcherFactoryAdapter(dispatcherFactory);
882         }
883 
884         return poolFactory;
885     }
886 
887     /**
888      * @return Returns the requesterFactory.
889      */
890     public MessageRequesterFactory getRequesterFactory()
891     {
892         return requesterFactory;
893     }
894 
895     /**
896      * @param requesterFactory The requesterFactory to set.
897      */
898     public void setRequesterFactory(MessageRequesterFactory requesterFactory)
899     {
900         KeyedPoolableObjectFactory poolFactory;
901 
902         if (requesterFactory instanceof KeyedPoolableObjectFactory)
903         {
904             poolFactory = (KeyedPoolableObjectFactory) requesterFactory;
905         }
906         else
907         {
908             // need to adapt the factory for use by commons-pool
909             poolFactory = new KeyedPoolMessageRequesterFactoryAdapter(requesterFactory);
910         }
911 
912         requesters.setFactory(poolFactory);
913 
914         // we keep a reference to the unadapted factory, otherwise people might end
915         // up with ClassCastExceptions on downcast to their implementation (sigh)
916         this.requesterFactory = requesterFactory;
917     }
918 
919     /**
920      * <p>The connector creates a {@link MuleMessageFactory} lazily and holds a reference to it for
921      * others to use.</p>
922      * <p>The typical use case is to share a single {@link MuleMessageFactory} between all
923      * {@link MessageDispatcher}, {@link MessageReceiver} and {@link MessageRequester} instances
924      * belonging to this connector.</p>
925      */
926     public MuleMessageFactory getMuleMessageFactory() throws CreateException
927     {
928         if (muleMessageFactory == null)
929         {
930             muleMessageFactory = createMuleMessageFactory();
931         }
932         return muleMessageFactory;
933     }
934 
935     /**
936      * The will cause the connector not to start when {@link #start()} is called. The
937      * only way to start the connector is to call
938      * {@link #setInitialStateStopped(boolean)} with 'false' and then calling
939      * {@link #start()}. This flag is used internally since some connectors that rely
940      * on external servers may need to wait for that server to become available
941      * before starting.
942      *
943      * @return true if the connector is not to be started with normal lifecycle,
944      *         flase otherwise
945      * @since 3.0.0
946      */
947     public boolean isInitialStateStopped()
948     {
949         return initialStateStopped;
950     }
951 
952     /**
953      * The will cause the connector not to start when {@link #start()} is called. The
954      * only way to start the connector is to call
955      * {@link #setInitialStateStopped(boolean)} with 'false' and then calling
956      * {@link #start()}. This flag is used internally since some connectors that rely
957      * on external servers may need to wait for that server to become available
958      * before starting. The only time this method should be used is when a
959      * subclassing connector needs to delay the start lifecycle due to a dependence
960      * on an external system. Most users can ignore this.
961      *
962      * @param initialStateStopped true to stop the connector starting through normal lifecycle. It will
963      *             be the responsibility of the code that sets this property to start the
964      *             connector
965      * @since 3.0.0
966      */
967     public void setInitialStateStopped(boolean initialStateStopped)
968     {
969         this.initialStateStopped = initialStateStopped;
970     }
971 
972     /**
973      * Returns the maximum number of dispatchers that can be concurrently active per
974      * endpoint.
975      *
976      * @return max. number of active dispatchers
977      */
978     public int getMaxDispatchersActive()
979     {
980         checkDispatchersInitialised();
981         return this.dispatchers.getMaxActive();
982     }
983 
984     /**
985      * Checks if the connector was initialised or not. Some connectors fields are created
986      * during connector's initialization so this check should be used before accessing them.
987      */
988     private void checkDispatchersInitialised()
989     {
990         if (dispatchers == null)
991         {
992             throw new IllegalStateException("Dispatchers pool was not initialised");
993         }
994     }
995 
996     /**
997      * Returns the maximum number of dispatchers that can be concurrently active for
998      * all endpoints.
999      *
1000      * @return max. total number of active dispatchers
1001      */
1002     public int getMaxTotalDispatchers()
1003     {
1004         checkDispatchersInitialised();
1005         return this.dispatchers.getMaxTotal();
1006     }
1007 
1008     /**
1009      * Configures the maximum number of dispatchers that can be concurrently active
1010      * per endpoint
1011      *
1012      * @param maxActive max. number of active dispatchers
1013      */
1014     public void setMaxDispatchersActive(int maxActive)
1015     {
1016         checkDispatchersInitialised();
1017         this.dispatchers.setMaxActive(maxActive);
1018         // adjust maxIdle in tandem to avoid thrashing
1019         this.dispatchers.setMaxIdle(maxActive);
1020         // this tells the pool to expire some objects eventually if we start
1021         // running out. This happens if one is using a lot of dynamic endpoints.
1022         this.dispatchers.setMaxTotal(20 * maxActive);
1023     }
1024 
1025     private MessageDispatcher getDispatcher(OutboundEndpoint endpoint) throws MuleException
1026     {
1027         if (!isStarted())
1028         {
1029             throw new LifecycleException(CoreMessages.lifecycleErrorCannotUseConnector(getName(),
1030                     lifecycleManager.getCurrentPhase()), this);
1031         }
1032 
1033         if (endpoint == null)
1034         {
1035             throw new IllegalArgumentException("Endpoint must not be null");
1036         }
1037 
1038         if (!supportsProtocol(endpoint.getConnector().getProtocol()))
1039         {
1040             throw new IllegalArgumentException(CoreMessages.connectorSchemeIncompatibleWithEndpointScheme(
1041                     this.getProtocol(), endpoint.getEndpointURI().toString()).getMessage());
1042         }
1043 
1044         MessageDispatcher dispatcher = null;
1045         try
1046         {
1047             if (logger.isDebugEnabled())
1048             {
1049                 logger.debug("Borrowing a dispatcher for endpoint: " + endpoint.getEndpointURI());
1050             }
1051 
1052             dispatcher = (MessageDispatcher) dispatchers.borrowObject(endpoint);
1053 
1054             if (logger.isDebugEnabled())
1055             {
1056                 logger.debug("Borrowed a dispatcher for endpoint: " + endpoint.getEndpointURI() + " = "
1057                         + dispatcher.toString());
1058             }
1059 
1060             return dispatcher;
1061         }
1062         catch (Exception ex)
1063         {
1064             throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
1065         }
1066         finally
1067         {
1068             try
1069             {
1070                 if (logger.isDebugEnabled())
1071                 {
1072                     logger.debug("Borrowed dispatcher: " + ObjectUtils.toString(dispatcher, "null"));
1073                 }
1074             }
1075             catch (Exception ex)
1076             {
1077                 throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
1078             }
1079         }
1080     }
1081 
1082     private void returnDispatcher(OutboundEndpoint endpoint, MessageDispatcher dispatcher)
1083     {
1084         if (endpoint != null && dispatcher != null)
1085         {
1086             if (logger.isDebugEnabled())
1087             {
1088                 logger.debug("Returning dispatcher for endpoint: " + endpoint.getEndpointURI() + " = "
1089                         + dispatcher.toString());
1090             }
1091 
1092             try
1093             {
1094                 dispatchers.returnObject(endpoint, dispatcher);
1095             }
1096             catch (Exception e)
1097             {
1098                 // ignore - if the dispatcher is broken, it will likely get cleaned
1099                 // up by the factory
1100                 logger.error("Failed to dispose dispatcher for endpoint: " + endpoint
1101                         + ". This will cause a memory leak. Please report to", e);
1102             }
1103 
1104         }
1105     }
1106 
1107     /**
1108      * Returns the maximum number of requesters that can be concurrently active per
1109      * endpoint.
1110      *
1111      * @return max. number of active requesters
1112      */
1113     public int getMaxRequestersActive()
1114     {
1115         return this.requesters.getMaxActive();
1116     }
1117 
1118     /**
1119      * Configures the maximum number of requesters that can be concurrently active
1120      * per endpoint
1121      *
1122      * @param maxActive max. number of active requesters
1123      */
1124     public void setMaxRequestersActive(int maxActive)
1125     {
1126         this.requesters.setMaxActive(maxActive);
1127         // adjust maxIdle in tandem to avoid thrashing
1128         this.requesters.setMaxIdle(maxActive);
1129         // this tells the pool to expire some objects eventually if we start
1130         // running out. This happens if one is using a lot of dynamic endpoints.
1131         this.requesters.setMaxTotal(20 * maxActive);
1132     }
1133 
1134     private MessageRequester getRequester(InboundEndpoint endpoint) throws MuleException
1135     {
1136         if (!isStarted())
1137         {
1138             throw new LifecycleException(CoreMessages.lifecycleErrorCannotUseConnector(getName(),
1139                     lifecycleManager.getCurrentPhase()), this);
1140         }
1141 
1142         if (endpoint == null)
1143         {
1144             throw new IllegalArgumentException("Endpoint must not be null");
1145         }
1146 
1147         if (!supportsProtocol(endpoint.getConnector().getProtocol()))
1148         {
1149             throw new IllegalArgumentException(CoreMessages.connectorSchemeIncompatibleWithEndpointScheme(
1150                     this.getProtocol(), endpoint.getEndpointURI().toString()).getMessage());
1151         }
1152 
1153         MessageRequester requester = null;
1154         try
1155         {
1156             if (logger.isDebugEnabled())
1157             {
1158                 logger.debug("Borrowing a requester for endpoint: " + endpoint.getEndpointURI());
1159             }
1160 
1161             requester = (MessageRequester) requesters.borrowObject(endpoint);
1162 
1163             if (logger.isDebugEnabled())
1164             {
1165                 logger.debug("Borrowed a requester for endpoint: " + endpoint.getEndpointURI() + " = "
1166                         + requester.toString());
1167             }
1168 
1169             return requester;
1170         }
1171         catch (Exception ex)
1172         {
1173             throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
1174         }
1175         finally
1176         {
1177             try
1178             {
1179                 if (logger.isDebugEnabled())
1180                 {
1181                     logger.debug("Borrowed requester: " + ObjectUtils.toString(requester, "null"));
1182                 }
1183             }
1184             catch (Exception ex)
1185             {
1186                 throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
1187             }
1188         }
1189     }
1190 
1191     private void returnRequester(InboundEndpoint endpoint, MessageRequester requester)
1192     {
1193         if (endpoint != null && requester != null)
1194         {
1195             if (logger.isDebugEnabled())
1196             {
1197                 logger.debug("Returning requester for endpoint: " + endpoint.getEndpointURI() + " = "
1198                         + requester.toString());
1199             }
1200 
1201             try
1202             {
1203                 requesters.returnObject(endpoint, requester);
1204             }
1205             catch (Exception e)
1206             {
1207                 // ignore - if the requester is broken, it will likely get cleaned
1208                 // up by the factory
1209                 logger.error("Failed to dispose requester for endpoint: " + endpoint
1210                         + ". This will cause a memory leak. Please report to", e);
1211             }
1212         }
1213 
1214     }
1215 
1216     public void registerListener(InboundEndpoint endpoint,
1217                                  MessageProcessor messageProcessorChain,
1218                                  FlowConstruct flowConstruct) throws Exception
1219     {
1220         if (endpoint == null)
1221         {
1222             throw new IllegalArgumentException("The endpoint cannot be null when registering a listener");
1223         }
1224 
1225         if (messageProcessorChain == null)
1226         {
1227             throw new IllegalArgumentException("The messageProcessorChain cannot be null when registering a listener");
1228         }
1229 
1230         EndpointURI endpointUri = endpoint.getEndpointURI();
1231         if (endpointUri == null)
1232         {
1233             throw new ConnectorException(CoreMessages.endpointIsNullForListener(), this);
1234         }
1235 
1236         logger.info("Registering listener: " + flowConstruct.getName() + " on endpointUri: "
1237                 + endpointUri.toString());
1238 
1239         if (getReceiver(flowConstruct, endpoint) != null)
1240         {
1241             throw new ConnectorException(CoreMessages.listenerAlreadyRegistered(endpointUri), this);
1242         }
1243 
1244         MessageReceiver receiver = createReceiver(flowConstruct, endpoint);
1245         receiver.setListener(messageProcessorChain);
1246 
1247         Object receiverKey = getReceiverKey(flowConstruct, endpoint);
1248         receiver.setReceiverKey(receiverKey.toString());
1249         // Since we're managing the creation we also need to initialise
1250         receiver.initialise();
1251         receivers.put(receiverKey, receiver);
1252 
1253         if (isConnected())
1254         {
1255             receiver.connect();
1256         }
1257 
1258         if (isStarted())
1259         {
1260             receiver.start();
1261         }
1262     }
1263 
1264     /**
1265      * The method determines the key used to store the receiver against.
1266      *
1267      * @param flowConstruct the service for which the endpoint is being registered
1268      * @param endpoint      the endpoint being registered for the service
1269      * @return the key to store the newly created receiver against
1270      */
1271     protected Object getReceiverKey(FlowConstruct flowConstruct, InboundEndpoint endpoint)
1272     {
1273         return StringUtils.defaultIfEmpty(endpoint.getEndpointURI().getFilterAddress(),
1274                 endpoint.getEndpointURI().getAddress());
1275     }
1276 
1277     public final void unregisterListener(InboundEndpoint endpoint, FlowConstruct flowConstruct) throws Exception
1278     {
1279         if (endpoint == null)
1280         {
1281             throw new IllegalArgumentException("The endpoint must not be null when you unregister a listener");
1282         }
1283 
1284         EndpointURI endpointUri = endpoint.getEndpointURI();
1285         if (endpointUri == null)
1286         {
1287             throw new IllegalArgumentException(
1288                     "The endpointUri must not be null when you unregister a listener");
1289         }
1290 
1291         if (logger.isInfoEnabled())
1292         {
1293             logger.info("Removing listener on endpointUri: " + endpointUri);
1294         }
1295 
1296         if (receivers != null && !receivers.isEmpty())
1297         {
1298             MessageReceiver receiver = receivers.remove(getReceiverKey(flowConstruct, endpoint));
1299             if (receiver != null)
1300             {
1301                 // This will automatically stop and disconnect before disposing.
1302                 destroyReceiver(receiver, endpoint);
1303                 doUnregisterListener(flowConstruct, endpoint, receiver);
1304             }
1305         }
1306     }
1307 
1308     protected void doUnregisterListener(FlowConstruct flowConstruct, InboundEndpoint endpoint, MessageReceiver receiver)
1309     {
1310         // Template method
1311     }
1312 
1313     /**
1314      * Getter for property 'dispatcherThreadingProfile'.
1315      *
1316      * @return Value for property 'dispatcherThreadingProfile'.
1317      */
1318     public ThreadingProfile getDispatcherThreadingProfile()
1319     {
1320         if (dispatcherThreadingProfile == null && muleContext != null)
1321         {
1322             dispatcherThreadingProfile = muleContext.getDefaultMessageDispatcherThreadingProfile();
1323         }
1324         return dispatcherThreadingProfile;
1325     }
1326 
1327     /**
1328      * Setter for property 'dispatcherThreadingProfile'.
1329      *
1330      * @param dispatcherThreadingProfile Value to set for property
1331      *                                   'dispatcherThreadingProfile'.
1332      */
1333     public void setDispatcherThreadingProfile(ThreadingProfile dispatcherThreadingProfile)
1334     {
1335         this.dispatcherThreadingProfile = dispatcherThreadingProfile;
1336     }
1337 
1338     /**
1339      * Getter for property 'requesterThreadingProfile'.
1340      *
1341      * @return Value for property 'requesterThreadingProfile'.
1342      */
1343     public ThreadingProfile getRequesterThreadingProfile()
1344     {
1345         if (requesterThreadingProfile == null && muleContext != null)
1346         {
1347             requesterThreadingProfile = muleContext.getDefaultMessageRequesterThreadingProfile();
1348         }
1349         return requesterThreadingProfile;
1350     }
1351 
1352     /**
1353      * Setter for property 'requesterThreadingProfile'.
1354      *
1355      * @param requesterThreadingProfile Value to set for property
1356      *                                  'requesterThreadingProfile'.
1357      */
1358     public void setRequesterThreadingProfile(ThreadingProfile requesterThreadingProfile)
1359     {
1360         this.requesterThreadingProfile = requesterThreadingProfile;
1361     }
1362 
1363     /**
1364      * Getter for property 'receiverThreadingProfile'.
1365      *
1366      * @return Value for property 'receiverThreadingProfile'.
1367      */
1368     public ThreadingProfile getReceiverThreadingProfile()
1369     {
1370         if (receiverThreadingProfile == null && muleContext != null)
1371         {
1372             receiverThreadingProfile = muleContext.getDefaultMessageReceiverThreadingProfile();
1373         }
1374         return receiverThreadingProfile;
1375     }
1376 
1377     /**
1378      * Setter for property 'receiverThreadingProfile'.
1379      *
1380      * @param receiverThreadingProfile Value to set for property
1381      *                                 'receiverThreadingProfile'.
1382      */
1383     public void setReceiverThreadingProfile(ThreadingProfile receiverThreadingProfile)
1384     {
1385         this.receiverThreadingProfile = receiverThreadingProfile;
1386     }
1387 
1388     public void destroyReceiver(MessageReceiver receiver, ImmutableEndpoint endpoint) throws Exception
1389     {
1390         receiver.dispose();
1391     }
1392 
1393     protected abstract void doInitialise() throws InitialisationException;
1394 
1395     /**
1396      * Template method to perform any work when destroying the connectoe
1397      */
1398     protected abstract void doDispose();
1399 
1400     /**
1401      * Template method to perform any work when starting the connectoe
1402      *
1403      * @throws MuleException if the method fails
1404      */
1405     protected abstract void doStart() throws MuleException;
1406 
1407     /**
1408      * Template method to perform any work when stopping the connectoe
1409      *
1410      * @throws MuleException if the method fails
1411      */
1412     protected abstract void doStop() throws MuleException;
1413 
1414     public List<Transformer> getDefaultInboundTransformers(ImmutableEndpoint endpoint)
1415     {
1416         if (serviceDescriptor == null)
1417         {
1418             throw new RuntimeException("serviceDescriptor not initialized");
1419         }
1420         return TransformerUtils.getDefaultInboundTransformers(serviceDescriptor, endpoint);
1421     }
1422 
1423     public List<Transformer> getDefaultResponseTransformers(ImmutableEndpoint endpoint)
1424     {
1425         if (serviceDescriptor == null)
1426         {
1427             throw new RuntimeException("serviceDescriptor not initialized");
1428         }
1429         return TransformerUtils.getDefaultResponseTransformers(serviceDescriptor, endpoint);
1430     }
1431 
1432     public List<Transformer> getDefaultOutboundTransformers(ImmutableEndpoint endpoint)
1433     {
1434         if (serviceDescriptor == null)
1435         {
1436             throw new RuntimeException("serviceDescriptor not initialized");
1437         }
1438         return TransformerUtils.getDefaultOutboundTransformers(serviceDescriptor, endpoint);
1439     }
1440 
1441     /**
1442      * Getter for property 'replyToHandler'.
1443      *
1444      * @return Value for property 'replyToHandler'.
1445      */
1446     public ReplyToHandler getReplyToHandler(ImmutableEndpoint endpoint)
1447     {
1448         return new DefaultReplyToHandler(getDefaultResponseTransformers(endpoint), muleContext);
1449     }
1450 
1451     /**
1452      * Fires a server notification to all registered listeners
1453      *
1454      * @param notification the notification to fire.
1455      */
1456     public void fireNotification(ServerNotification notification)
1457     {
1458         cachedNotificationHandler.fireNotification(notification);
1459     }
1460 
1461     public boolean isResponseEnabled()
1462     {
1463         return false;
1464     }
1465 
1466     public MessageReceiver getReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint)
1467     {
1468         if (receivers != null)
1469         {
1470             Object key = getReceiverKey(flowConstruct, endpoint);
1471             if (key != null)
1472             {
1473                 return receivers.get(key);
1474             }
1475             else
1476             {
1477                 throw new RuntimeException("getReceiverKey() returned a null key");
1478             }
1479         }
1480         else
1481         {
1482             throw new RuntimeException("Connector has not been initialized.");
1483         }
1484     }
1485 
1486     /**
1487      * Getter for property 'receivers'.
1488      *
1489      * @return Value for property 'receivers'.
1490      */
1491     public Map<Object, MessageReceiver> getReceivers()
1492     {
1493         return Collections.unmodifiableMap(receivers);
1494     }
1495 
1496     public MessageReceiver lookupReceiver(String key)
1497     {
1498         if (key != null)
1499         {
1500             return receivers.get(key);
1501         }
1502         else
1503         {
1504             throw new IllegalArgumentException("Receiver key must not be null");
1505         }
1506     }
1507 
1508     public MessageReceiver[] getReceivers(String wildcardExpression)
1509     {
1510         WildcardFilter filter = new WildcardFilter(wildcardExpression);
1511         filter.setCaseSensitive(false);
1512 
1513         List<MessageReceiver> found = new ArrayList<MessageReceiver>();
1514 
1515         for (Map.Entry<Object, MessageReceiver> e : receivers.entrySet())
1516         {
1517             if (filter.accept(e.getKey()))
1518             {
1519                 found.add(e.getValue());
1520             }
1521         }
1522 
1523         return CollectionUtils.toArrayOfComponentType(found, MessageReceiver.class);
1524     }
1525 
1526     public void connect() throws Exception
1527     {
1528         if (lifecycleManager.getState().isDisposed())
1529         {
1530             throw new LifecycleException(CoreMessages.lifecycleErrorCannotUseConnector(getName(),
1531                     lifecycleManager.getCurrentPhase()), this);
1532         }
1533 
1534         if (isConnected())
1535         {
1536             return;
1537         }
1538 
1539         if (logger.isDebugEnabled())
1540         {
1541             logger.debug("Connecting: " + this);
1542         }
1543 
1544         RetryCallback callback = new RetryCallback()
1545         {
1546             public void doWork(RetryContext context) throws Exception
1547             {
1548                 // Try validateConnection() rather than connect() which may be a less expensive operation while we're retrying.
1549                 if (validateConnections && context.getLastFailure() instanceof ConnectException)
1550                 {
1551                     Connectable failed = ((ConnectException) context.getLastFailure()).getFailed();                    
1552                     if (!failed.validateConnection(context).isOk())
1553                     {
1554                         throw new ConnectException(
1555                                 MessageFactory.createStaticMessage("Still unable to connect to resource " + failed.getClass().getName()),
1556                                 context.getLastFailure(), failed);
1557                     }
1558                 }
1559                 doConnect();
1560 
1561                 if (receivers != null)
1562                 {
1563                     for (MessageReceiver receiver : receivers.values())
1564                     {
1565                         final List<MuleException> errors = new ArrayList<MuleException>();
1566                         try
1567                         {
1568                             if (logger.isDebugEnabled())
1569                             {
1570                                 logger.debug("Connecting receiver on endpoint: " + receiver.getEndpoint().getEndpointURI());
1571                             }
1572                             receiver.connect();
1573                             if (isStarted())
1574                             {
1575                                 receiver.start();
1576                             }
1577                         }
1578                         catch (MuleException e)
1579                         {
1580                             logger.error(e);
1581                             errors.add(e);
1582                         }
1583 
1584                         if (!errors.isEmpty())
1585                         {
1586                             // throw the first one in order not to break the reconnection
1587                             // strategy logic,
1588                             // every exception has been logged above already
1589                             // api needs refactoring to support the multi-cause exception
1590                             // here
1591                             throw errors.get(0);
1592                         }
1593                     }
1594                 }
1595                 
1596                 setConnected(true);
1597                 logger.info("Connected: " + getWorkDescription());
1598                 
1599                 if (startOnConnect && !isStarted() && !isStarting())
1600                 {
1601                     startAfterConnect();
1602                 }
1603             }
1604 
1605             public String getWorkDescription()
1606             {
1607                 return getConnectionDescription();
1608             }
1609         };
1610 
1611         retryPolicyTemplate.execute(callback, muleContext.getWorkManager());
1612     }
1613 
1614     /**
1615      * Override this method to test whether the connector is able to connect to its
1616      * resource(s). This will allow a retry policy to go into effect in the case of
1617      * failure.
1618      *
1619      * @return retry context with a success flag or failure details
1620      * @see RetryContext#isOk()
1621      * @see RetryContext#getLastFailure()
1622      */
1623     public RetryContext validateConnection(RetryContext retryContext)
1624     {
1625         retryContext.setOk();
1626         return retryContext;
1627     }
1628 
1629     public void disconnect() throws Exception
1630     {
1631         startOnConnect = isStarted();
1632 
1633         if (receivers != null)
1634         {
1635             for (MessageReceiver receiver : receivers.values())
1636             {
1637                 if (logger.isDebugEnabled())
1638                 {
1639                     logger.debug("Disconnecting receiver on endpoint: "
1640                             + receiver.getEndpoint().getEndpointURI());
1641                 }
1642                 try
1643                 {
1644                     receiver.disconnect();
1645                 }
1646                 catch (Exception e)
1647                 {
1648                     logger.error(e.getMessage(), e);
1649                 }                
1650             }
1651         }
1652         try
1653         {
1654 	        if (isStarted() && !isStopping())
1655     	    {
1656         	    stop();
1657 	        }
1658             this.doDisconnect();
1659             if (logger.isInfoEnabled())
1660             {
1661                 logger.info("Disconnected: " + this.getConnectionDescription());
1662             }
1663             this.fireNotification(new ConnectionNotification(this, getConnectEventId(),
1664                     ConnectionNotification.CONNECTION_DISCONNECTED));
1665         }
1666         catch (Exception e)
1667         {
1668             logger.error(e.getMessage());
1669         }                
1670         finally
1671         {
1672             connected.set(false);
1673         }
1674     }
1675 
1676     public String getConnectionDescription()
1677     {
1678         return this.toString();
1679     }
1680 
1681     public final boolean isConnected()
1682     {
1683         return connected.get();
1684     }
1685 
1686     public final void setConnected(boolean flag)
1687     {
1688         connected.set(flag);
1689     }
1690 
1691     public final void setReconnecting(boolean flag)
1692     {
1693         reconnecting.set(flag);
1694     }
1695 
1696     public final boolean isReconnecting()
1697     {
1698         return reconnecting.get();
1699     }
1700 
1701     /**
1702      * Template method where any connections should be made for the connector
1703      *
1704      * @throws Exception
1705      */
1706     protected abstract void doConnect() throws Exception;
1707 
1708     /**
1709      * Template method where any connected resources used by the connector should be
1710      * disconnected
1711      *
1712      * @throws Exception
1713      */
1714     protected abstract void doDisconnect() throws Exception;
1715 
1716     /**
1717      * The resource id used when firing ConnectEvents from this connector
1718      *
1719      * @return the resource id used when firing ConnectEvents from this connector
1720      */
1721     protected String getConnectEventId()
1722     {
1723         return getName();
1724     }
1725 
1726     /**
1727      * For better throughput when using TransactedMessageReceivers this will enable a
1728      * number of concurrent receivers, based on the value returned by
1729      * {@link #getNumberOfConcurrentTransactedReceivers()}. This property is used by
1730      * transports that support transactions, specifically receivers that extend the
1731      * TransactedPollingMessageReceiver.
1732      *
1733      * @return true if multiple receivers will be enabled for this connection
1734      */
1735     public boolean isCreateMultipleTransactedReceivers()
1736     {
1737         return createMultipleTransactedReceivers;
1738     }
1739 
1740     /**
1741      * @param createMultipleTransactedReceivers
1742      *         if true, multiple receivers will be
1743      *         created for this connection
1744      * @see #isCreateMultipleTransactedReceivers()
1745      */
1746     public void setCreateMultipleTransactedReceivers(boolean createMultipleTransactedReceivers)
1747     {
1748         this.createMultipleTransactedReceivers = createMultipleTransactedReceivers;
1749     }
1750 
1751     /**
1752      * Returns the number of concurrent receivers that will be launched when
1753      * {@link #isCreateMultipleTransactedReceivers()} returns <code>true</code>.
1754      *
1755      * @see #DEFAULT_NUM_CONCURRENT_TX_RECEIVERS
1756      */
1757     public int getNumberOfConcurrentTransactedReceivers()
1758     {
1759         return this.numberOfConcurrentTransactedReceivers;
1760     }
1761 
1762     /**
1763      * @param count the number of concurrent transacted receivers to start
1764      * @see #getNumberOfConcurrentTransactedReceivers()
1765      */
1766     public void setNumberOfConcurrentTransactedReceivers(int count)
1767     {
1768         this.numberOfConcurrentTransactedReceivers = count;
1769     }
1770 
1771     public void setDynamicNotification(boolean dynamic)
1772     {
1773         dynamicNotification = dynamic;
1774     }
1775 
1776     protected void updateCachedNotificationHandler()
1777     {
1778         if (null != muleContext)
1779         {
1780             if (dynamicNotification)
1781             {
1782                 cachedNotificationHandler = muleContext.getNotificationManager();
1783             }
1784             else
1785             {
1786                 cachedNotificationHandler = new OptimisedNotificationHandler(
1787                         muleContext.getNotificationManager(), EndpointMessageNotification.class);
1788             }
1789         }
1790     }
1791 
1792     public boolean isEnableMessageEvents()
1793     {
1794         return cachedNotificationHandler.isNotificationEnabled(EndpointMessageNotification.class);
1795     }
1796 
1797     /**
1798      * Registers other protocols 'understood' by this connector. These must contain
1799      * scheme meta info. Any protocol registered must begin with the protocol of this
1800      * connector, i.e. If the connector is axis the protocol for jms over axis will
1801      * be axis:jms. Here, 'axis' is the scheme meta info and 'jms' is the protocol.
1802      * If the protocol argument does not start with the connector's protocol, it will
1803      * be appended.
1804      *
1805      * @param protocol the supported protocol to register
1806      */
1807     public void registerSupportedProtocol(String protocol)
1808     {
1809         protocol = protocol.toLowerCase();
1810         if (protocol.startsWith(getProtocol().toLowerCase()))
1811         {
1812             registerSupportedProtocolWithoutPrefix(protocol);
1813         }
1814         else
1815         {
1816             supportedProtocols.add(getProtocol().toLowerCase() + ":" + protocol);
1817         }
1818     }
1819 
1820     /**
1821      * Used by Meta endpoint descriptors to register support for endpoint of the meta
1822      * endpoint type. For example an RSS endpoint uses the Http connector. By
1823      * registering 'rss' as a supported meta protocol, this connector can be used
1824      * when creating RSS endpoints.
1825      *
1826      * @param protocol the meta protocol that can be used with this connector
1827      * @since 3.0.0
1828      */
1829     public void registerSupportedMetaProtocol(String protocol)
1830     {
1831         supportedProtocols.add(protocol.toLowerCase() + ":" + getProtocol().toLowerCase());
1832 
1833     }
1834 
1835     /**
1836      * Registers other protocols 'understood' by this connector. These must contain
1837      * scheme meta info. Unlike the {@link #registerSupportedProtocol(String)}
1838      * method, this allows you to register protocols that are not prefixed with the
1839      * connector protocol. This is useful where you use a Service Finder to discover
1840      * which Transport implementation to use. For example the 'wsdl' transport is a
1841      * generic 'finder' transport that will use Axis or CXF to create the WSDL
1842      * client. These transport protocols would be wsdl-axis and wsdl-cxf, but they
1843      * can all support 'wsdl' protocol too.
1844      *
1845      * @param protocol the supported protocol to register
1846      */
1847     protected void registerSupportedProtocolWithoutPrefix(String protocol)
1848     {
1849         supportedProtocols.add(protocol.toLowerCase());
1850     }
1851 
1852     public void unregisterSupportedProtocol(String protocol)
1853     {
1854         protocol = protocol.toLowerCase();
1855         if (protocol.startsWith(getProtocol().toLowerCase()))
1856         {
1857             supportedProtocols.remove(protocol);
1858         }
1859         else
1860         {
1861             supportedProtocols.remove(getProtocol().toLowerCase() + ":" + protocol);
1862         }
1863     }
1864 
1865     /**
1866      * @return true if the protocol is supported by this connector.
1867      */
1868     public boolean supportsProtocol(String protocol)
1869     {
1870         return supportedProtocols.contains(protocol.toLowerCase());
1871     }
1872 
1873     /**
1874      * Returns an unmodifiable list of the protocols supported by this connector
1875      *
1876      * @return an unmodifiable list of the protocols supported by this connector
1877      */
1878     public List getSupportedProtocols()
1879     {
1880         return Collections.unmodifiableList(supportedProtocols);
1881     }
1882 
1883     /**
1884      * Sets A list of protocols that the connector can accept
1885      *
1886      * @param supportedProtocols
1887      */
1888     public void setSupportedProtocols(List supportedProtocols)
1889     {
1890         for (Iterator iterator = supportedProtocols.iterator(); iterator.hasNext();)
1891         {
1892             String s = (String) iterator.next();
1893             registerSupportedProtocol(s);
1894         }
1895     }
1896 
1897     /**
1898      * Returns a work manager for message receivers.
1899      */
1900     protected WorkManager getReceiverWorkManager() throws MuleException
1901     {
1902         return (WorkManager) receiverWorkManager.get();
1903     }
1904 
1905     /**
1906      * Returns a work manager for message dispatchers.
1907      *
1908      * @throws MuleException in case of error
1909      */
1910     protected WorkManager getDispatcherWorkManager() throws MuleException
1911     {
1912         return (WorkManager) dispatcherWorkManager.get();
1913     }
1914 
1915     /**
1916      * Returns a work manager for message requesters.
1917      *
1918      * @throws MuleException in case of error
1919      */
1920     protected WorkManager getRequesterWorkManager() throws MuleException
1921     {
1922         return (WorkManager) requesterWorkManager.get();
1923     }
1924 
1925     /**
1926      * Returns a Scheduler service for periodic tasks, currently limited to internal
1927      * use. Note: getScheduler() currently conflicts with the same method in the
1928      * Quartz transport
1929      */
1930     public ScheduledExecutorService getScheduler()
1931     {
1932         return scheduler;
1933     }
1934 
1935     protected ScheduledExecutorService createScheduler()
1936     {
1937         // Use connector's classloader so that other temporary classloaders
1938         // aren't used when things are started lazily or from elsewhere.
1939         ThreadFactory threadFactory = new NamedThreadFactory(this.getName() + ".scheduler", muleContext.getExecutionClassLoader());
1940         ScheduledThreadPoolExecutor newExecutor = new ScheduledThreadPoolExecutor(4, threadFactory);
1941         newExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
1942         newExecutor.setKeepAliveTime(this.getReceiverThreadingProfile().getThreadTTL(), TimeUnit.MILLISECONDS);
1943         newExecutor.allowCoreThreadTimeOut(true);
1944         return newExecutor;
1945     }
1946 
1947     /**
1948      * Getter for property 'sessionHandler'.
1949      *
1950      * @return Value for property 'sessionHandler'.
1951      */
1952     public SessionHandler getSessionHandler()
1953     {
1954         return sessionHandler;
1955     }
1956 
1957     /**
1958      * Setter for property 'sessionHandler'.
1959      *
1960      * @param sessionHandler Value to set for property 'sessionHandler'.
1961      */
1962     public void setSessionHandler(SessionHandler sessionHandler)
1963     {
1964         this.sessionHandler = sessionHandler;
1965     }
1966 
1967     public void workAccepted(WorkEvent event)
1968     {
1969         this.handleWorkException(event, "workAccepted");
1970     }
1971 
1972     public void workRejected(WorkEvent event)
1973     {
1974         this.handleWorkException(event, "workRejected");
1975     }
1976 
1977     public void workStarted(WorkEvent event)
1978     {
1979         this.handleWorkException(event, "workStarted");
1980     }
1981 
1982     public void workCompleted(WorkEvent event)
1983     {
1984         this.handleWorkException(event, "workCompleted");
1985     }
1986 
1987     protected void handleWorkException(WorkEvent event, String type)
1988     {
1989         if (event == null)
1990         {
1991             return;
1992         }
1993 
1994         Throwable e = event.getException();
1995 
1996         if (e == null)
1997         {
1998             return;
1999         }
2000 
2001         if (e.getCause() != null)
2002         {
2003             e = e.getCause();
2004         }
2005 
2006         logger.error("Work caused exception on '" + type + "'. Work being executed was: "
2007                 + event.getWork().toString());
2008 
2009         if (e instanceof Exception)
2010         {
2011             this.handleException((Exception) e);
2012         }
2013         else
2014         {
2015             throw new MuleRuntimeException(CoreMessages.connectorCausedError(this.getName()), e);
2016         }
2017     }
2018 
2019     /**
2020      * This method will return the dispatcher to the pool or, if the payload is an
2021      * inputstream, replace the payload with a new DelegatingInputStream which
2022      * returns the dispatcher to the pool when the stream is closed.
2023      *
2024      * @param endpoint
2025      * @param dispatcher
2026      * @param result
2027      */
2028     protected void setupDispatchReturn(final OutboundEndpoint endpoint,
2029                                        final MessageDispatcher dispatcher,
2030                                        MuleMessage result)
2031     {
2032         if (result != null && result.getPayload() instanceof InputStream)
2033         {
2034             DelegatingInputStream is = new DelegatingInputStream((InputStream) result.getPayload())
2035             {
2036                 @Override
2037                 public void close() throws IOException
2038                 {
2039                     try
2040                     {
2041                         super.close();
2042                     }
2043                     finally
2044                     {
2045                         returnDispatcher(endpoint, dispatcher);
2046                     }
2047                 }
2048             };
2049             result.setPayload(is);
2050         }
2051         else
2052         {
2053 
2054             this.returnDispatcher(endpoint, dispatcher);
2055         }
2056     }
2057 
2058     public MuleMessage request(String uri, long timeout) throws Exception
2059     {
2060         return request(getMuleContext().getEndpointFactory().getInboundEndpoint(uri),
2061                 timeout);
2062     }
2063 
2064     public MuleMessage request(InboundEndpoint endpoint, long timeout) throws Exception
2065     {
2066         MessageRequester requester = null;
2067         MuleMessage result = null;
2068 
2069         try
2070         {
2071             requester = this.getRequester(endpoint);
2072             result = requester.request(timeout);
2073             return result;
2074         }
2075         finally
2076         {
2077             setupRequestReturn(endpoint, requester, result);
2078         }
2079     }
2080 
2081     /**
2082      * This method will return the requester to the pool or, if the payload is an
2083      * inputstream, replace the payload with a new DelegatingInputStream which
2084      * returns the requester to the pool when the stream is closed.
2085      *
2086      * @param endpoint
2087      * @param requester
2088      * @param result
2089      */
2090     protected void setupRequestReturn(final InboundEndpoint endpoint,
2091                                       final MessageRequester requester,
2092                                       MuleMessage result)
2093     {
2094         if (result != null && result.getPayload() instanceof InputStream)
2095         {
2096             DelegatingInputStream is = new DelegatingInputStream((InputStream) result.getPayload())
2097             {
2098                 @Override
2099                 public void close() throws IOException
2100                 {
2101                     try
2102                     {
2103                         super.close();
2104                     }
2105                     finally
2106                     {
2107                         returnRequester(endpoint, requester);
2108                     }
2109                 }
2110             };
2111             result.setPayload(is);
2112         }
2113         else
2114         {
2115 
2116             this.returnRequester(endpoint, requester);
2117         }
2118     }
2119 
2120     // -------- Methods from the removed AbstractServiceEnabled Connector
2121 
2122     /**
2123      * When this connector is created via the
2124      * {@link org.mule.transport.service.TransportFactory} the endpoint used to
2125      * determine the connector type is passed to this method so that any properties
2126      * set on the endpoint that can be used to initialise the connector are made
2127      * available.
2128      *
2129      * @param endpointUri the {@link EndpointURI} use to create this connector
2130      * @throws InitialisationException If there are any problems with the
2131      *                                 configuration set on the Endpoint or if another exception is
2132      *                                 thrown it is wrapped in an InitialisationException.
2133      */
2134     public void initialiseFromUrl(EndpointURI endpointUri) throws InitialisationException
2135     {
2136         if (!supportsProtocol(endpointUri.getFullScheme()))
2137         {
2138             throw new InitialisationException(CoreMessages.schemeNotCompatibleWithConnector(
2139                     endpointUri.getFullScheme(), this.getClass()), this);
2140         }
2141         Properties props = new Properties();
2142         props.putAll(endpointUri.getParams());
2143         // auto set username and password
2144         if (endpointUri.getUserInfo() != null)
2145         {
2146             props.setProperty("username", endpointUri.getUser());
2147             String passwd = endpointUri.getPassword();
2148             if (passwd != null)
2149             {
2150                 props.setProperty("password", passwd);
2151             }
2152         }
2153         String host = endpointUri.getHost();
2154         if (host != null)
2155         {
2156             props.setProperty("hostname", host);
2157             props.setProperty("host", host);
2158         }
2159         if (endpointUri.getPort() > -1)
2160         {
2161             props.setProperty("port", String.valueOf(endpointUri.getPort()));
2162         }
2163 
2164         org.mule.util.BeanUtils.populateWithoutFail(this, props, true);
2165 
2166         setName(new ObjectNameHelper(muleContext).getConnectorName(this));
2167         // initialise();
2168     }
2169 
2170     /**
2171      * Initialises this connector from its {@link TransportServiceDescriptor} This
2172      * will be called before the {@link #doInitialise()} method is called.
2173      *
2174      * @throws InitialisationException InitialisationException If there are any
2175      *                                 problems with the configuration or if another exception is thrown
2176      *                                 it is wrapped in an InitialisationException.
2177      */
2178     protected synchronized void initFromServiceDescriptor() throws InitialisationException
2179     {
2180         try
2181         {
2182             serviceDescriptor = (TransportServiceDescriptor) muleContext.getRegistry()
2183                     .lookupServiceDescriptor(ServiceType.TRANSPORT, getProtocol().toLowerCase(), serviceOverrides);
2184             if (serviceDescriptor == null)
2185             {
2186                 throw new ServiceException(CoreMessages.noServiceTransportDescriptor(getProtocol()));
2187             }
2188 
2189             if (logger.isDebugEnabled())
2190             {
2191                 logger.debug("Loading DispatcherFactory for connector: " + getName() + " ("
2192                         + getClass().getName() + ")");
2193             }
2194 
2195             MessageDispatcherFactory df = serviceDescriptor.createDispatcherFactory();
2196             if (df != null)
2197             {
2198                 this.setDispatcherFactory(df);
2199             }
2200             else if (logger.isDebugEnabled())
2201             {
2202                 logger.debug("Transport '" + getProtocol() + "' will not support outbound endpoints: ");
2203             }
2204 
2205             if (logger.isDebugEnabled())
2206             {
2207                 logger.debug("Loading RequesterFactory for connector: " + getName() + " ("
2208                         + getClass().getName() + ")");
2209             }
2210 
2211             MessageRequesterFactory rf = serviceDescriptor.createRequesterFactory();
2212             if (rf != null)
2213             {
2214                 this.setRequesterFactory(rf);
2215             }
2216             else if (logger.isDebugEnabled())
2217             {
2218                 logger.debug("Transport '" + getProtocol() + "' will not support requests: ");
2219             }
2220 
2221             sessionHandler = serviceDescriptor.createSessionHandler();
2222         }
2223         catch (Exception e)
2224         {
2225             throw new InitialisationException(e, this);
2226         }
2227     }
2228 
2229     /**
2230      * Get the {@link TransportServiceDescriptor} for this connector. This will be
2231      * null if the connector was created by the developer. To create a connector the
2232      * proper way the developer should use the {@link TransportFactory} and pass in
2233      * an endpoint.
2234      *
2235      * @return the {@link TransportServiceDescriptor} for this connector
2236      */
2237     protected TransportServiceDescriptor getServiceDescriptor()
2238     {
2239         if (serviceDescriptor == null)
2240         {
2241             throw new IllegalStateException("This connector has not yet been initialised: " + name);
2242         }
2243         return serviceDescriptor;
2244     }
2245 
2246     /**
2247      * Create a Message receiver for this connector
2248      *
2249      * @param flowConstruct the service that will receive events from this receiver, the
2250      *                      listener
2251      * @param endpoint      the endpoint that defies this inbound communication
2252      * @return an instance of the message receiver defined in this connectors'
2253      *         {@link org.mule.transport.service.TransportServiceDescriptor}
2254      *         initialised using the service and endpoint.
2255      * @throws Exception if there is a problem creating the receiver. This exception
2256      *                   really depends on the underlying transport, thus any exception
2257      *                   could be thrown
2258      */
2259     protected MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
2260     {
2261         return getServiceDescriptor().createMessageReceiver(this, flowConstruct, endpoint);
2262     }
2263 
2264     /**
2265      * A map of fully qualified class names that should override those in the
2266      * connectors' service descriptor This map will be null if there are no overrides
2267      *
2268      * @return a map of override values or null
2269      */
2270     public Map getServiceOverrides()
2271     {
2272         return serviceOverrides;
2273     }
2274 
2275     /**
2276      * Set the Service overrides on this connector.
2277      *
2278      * @param serviceOverrides the override values to use
2279      */
2280     public void setServiceOverrides(Map serviceOverrides)
2281     {
2282         this.serviceOverrides = new Properties();
2283         this.serviceOverrides.putAll(serviceOverrides);
2284     }
2285 
2286     /**
2287      * Will get the output stream for this type of transport. Typically this will be
2288      * called only when Streaming is being used on an outbound endpoint. If Streaming
2289      * is not supported by this transport an {@link UnsupportedOperationException} is
2290      * thrown. Note that the stream MUST release resources on close. For help doing
2291      * so, see {@link org.mule.model.streaming.CallbackOutputStream}.
2292      *
2293      * @param endpoint the endpoint that releates to this Dispatcher
2294      * @param event  the current event being processed
2295      * @return the output stream to use for this request
2296      * @throws MuleException in case of any error
2297      */
2298     public OutputStream getOutputStream(OutboundEndpoint endpoint, MuleEvent event) throws MuleException
2299     {
2300         throw new UnsupportedOperationException(CoreMessages.streamingNotSupported(this.getProtocol()).toString());
2301     }
2302 
2303     public MuleContext getMuleContext()
2304     {
2305         return muleContext;
2306     }
2307 
2308     @Override
2309     public String toString()
2310     {
2311         final StringBuffer sb = new StringBuffer(120);
2312         final String nl = System.getProperty("line.separator");
2313         sb.append(ClassUtils.getSimpleName(this.getClass()));
2314         // format message for multi-line output, single-line is not readable
2315         sb.append(nl);
2316         sb.append("{");
2317         sb.append(nl);
2318         sb.append("  name=").append(name);
2319         sb.append(nl);
2320         sb.append("  lifecycle=").append(
2321                 lifecycleManager == null ? "<not in lifecycle>" : lifecycleManager.getCurrentPhase());
2322         sb.append(nl);
2323         sb.append("  this=").append(Integer.toHexString(System.identityHashCode(this)));
2324         sb.append(nl);
2325         sb.append("  numberOfConcurrentTransactedReceivers=").append(numberOfConcurrentTransactedReceivers);
2326         sb.append(nl);
2327         sb.append("  createMultipleTransactedReceivers=").append(createMultipleTransactedReceivers);
2328         sb.append(nl);
2329         sb.append("  connected=").append(connected);
2330         sb.append(nl);
2331         sb.append("  supportedProtocols=").append(supportedProtocols);
2332         sb.append(nl);
2333         sb.append("  serviceOverrides=");
2334         if (serviceOverrides != null)
2335         {
2336             for (Map.Entry<Object, Object> entry : serviceOverrides.entrySet())
2337             {
2338                 sb.append(nl);
2339                 sb.append("    ").append(String.format("%s=%s", entry.getKey(), entry.getValue()));
2340             }
2341         }
2342         else
2343         {
2344             sb.append("<none>");
2345         }
2346         sb.append(nl);
2347         sb.append('}');
2348         sb.append(nl);
2349         return sb.toString();
2350     }
2351 
2352     public RetryPolicyTemplate getRetryPolicyTemplate()
2353     {
2354         return retryPolicyTemplate;
2355     }
2356 
2357     public void setRetryPolicyTemplate(RetryPolicyTemplate retryPolicyTemplate)
2358     {
2359         this.retryPolicyTemplate = retryPolicyTemplate;
2360     }
2361 
2362     /**
2363      * Whether to test a connection on each take from pool.
2364      */
2365     public boolean isValidateConnections()
2366     {
2367         return validateConnections;
2368     }
2369 
2370     /**
2371      * Whether to test a connection on each take. A result is higher availability at
2372      * the expense of a potential slight performance hit (when a test connection is
2373      * made) or be very lightweight in other cases (like sending a hearbeat ping to
2374      * the server).
2375      * <p/>
2376      * Disable to obtain slight performance gain or if you are absolutely sure of the
2377      * server availability.
2378      * <p/>
2379      * It is up to the transport implementatin to support such validation, thus it
2380      * should be considered a hint only.
2381      * <p/>
2382      * The default value is <code>true</code>
2383      */
2384     public void setValidateConnections(final boolean validateConnections)
2385     {
2386         this.validateConnections = validateConnections;
2387     }
2388 
2389     // MULE-4751 Expose some dispatcher and requester object pool configuration
2390 
2391     /**
2392      * Allows an ExhaustedAction to be configured on the dispatcher object pool See:
2393      * {@link GenericKeyedObjectPool#setWhenExhaustedAction(byte)}
2394      */
2395     public void setDispatcherPoolWhenExhaustedAction(byte whenExhaustedAction)
2396     {
2397         checkDispatchersInitialised();
2398         dispatchers.setWhenExhaustedAction(whenExhaustedAction);
2399     }
2400 
2401     /**
2402      * Allows a maxWait timeout to be configured on the dispatcher object pool See:
2403      * {@link GenericKeyedObjectPool#setMaxWait(long)}
2404      */
2405     public void setDispatcherPoolMaxWait(int maxWait)
2406     {
2407         checkDispatchersInitialised();
2408         dispatchers.setMaxWait(maxWait);
2409     }
2410 
2411     /**
2412      * Allows to define a factory to create the dispatchers pool that will be used in the connector
2413      */
2414     public void setDispatcherPoolFactory(ConfigurableKeyedObjectPoolFactory dispatcherPoolFactory)
2415     {
2416         this.dispatcherPoolFactory = dispatcherPoolFactory;
2417     }
2418 
2419     public ConfigurableKeyedObjectPoolFactory getDispatcherPoolFactory()
2420     {
2421         return dispatcherPoolFactory;
2422     }
2423 
2424     /**
2425      * Allows an ExhaustedAction to be configured on the requester object pool See:
2426      * {@link GenericKeyedObjectPool#setWhenExhaustedAction(byte)}
2427      */
2428     public void setRequesterPoolWhenExhaustedAction(byte whenExhaustedAction)
2429     {
2430         requesters.setWhenExhaustedAction(whenExhaustedAction);
2431     }
2432 
2433     /**
2434      * Allows a maxWait timeout to be configured on the requester object pool See:
2435      * {@link GenericKeyedObjectPool#setMaxWait(long)}
2436      */
2437     public void setRequesterPoolMaxWait(int maxWait)
2438     {
2439         requesters.setMaxWait(maxWait);
2440     }
2441 
2442     public MessageProcessor createDispatcherMessageProcessor(OutboundEndpoint endpoint) throws MuleException
2443     {
2444         if (endpoint.getExchangePattern().hasResponse() || !getDispatcherThreadingProfile().isDoThreading())
2445         {
2446             return new DispatcherMessageProcessor();
2447         }
2448         else
2449         {
2450             DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder();
2451             builder.setName("dispatcher processor chain for '" + endpoint.getAddress() + "'");
2452             builder.chain(new OptionalAsyncInterceptingMessageProcessor(new WorkManagerSource()
2453             {
2454                 public WorkManager getWorkManager() throws MuleException
2455                 {
2456                     return getDispatcherWorkManager();
2457                 }
2458             }));
2459             builder.chain(new DispatcherMessageProcessor());
2460             return builder.build();
2461         }
2462     }
2463     
2464     public MessageExchangePattern getDefaultExchangePattern()
2465     {
2466         try
2467         {
2468             return serviceDescriptor.getDefaultExchangePattern();
2469         }
2470         catch (TransportServiceException tse)
2471         {
2472             throw new MuleRuntimeException(tse);
2473         }
2474     }
2475     
2476     public List<MessageExchangePattern> getInboundExchangePatterns()
2477     {
2478         try
2479         {
2480             return serviceDescriptor.getInboundExchangePatterns();
2481         }
2482         catch (TransportServiceException tse)
2483         {
2484             throw new MuleRuntimeException(tse);
2485         }
2486     }
2487 
2488     public List<MessageExchangePattern> getOutboundExchangePatterns()
2489     {
2490         try
2491         {
2492             return serviceDescriptor.getOutboundExchangePatterns();
2493         }
2494         catch (TransportServiceException tse)
2495         {
2496             throw new MuleRuntimeException(tse);
2497         }
2498     }
2499     
2500     class DispatcherMessageProcessor implements MessageProcessor
2501     {
2502         private MessageProcessor notificationMessageProcessor;
2503 
2504         public MuleEvent process(MuleEvent event) throws MuleException
2505         {
2506             OutboundEndpoint endpoint = (OutboundEndpoint) event.getEndpoint();
2507             MessageDispatcher dispatcher = null;
2508             try
2509             {
2510                 dispatcher = getDispatcher(endpoint);
2511                 MuleEvent result = dispatcher.process(event);
2512                 // We need to invoke notification message processor with request
2513                 // message only after successful send/dispatch
2514                 if (notificationMessageProcessor == null)
2515                 {
2516                     notificationMessageProcessor = new OutboundNotificationMessageProcessor(endpoint);
2517                 }
2518                 notificationMessageProcessor.process(event);
2519                 return result;
2520 
2521             }
2522             catch (DispatchException dex)
2523             {
2524                 throw dex;
2525             }
2526             catch (MuleException ex)
2527             {
2528                 throw new DispatchException(event, endpoint, ex);
2529             }
2530             finally
2531             {
2532                 returnDispatcher(endpoint, dispatcher);
2533             }
2534         }
2535         
2536         @Override
2537         public String toString()
2538         {
2539             return ObjectUtils.toString(this);
2540         }
2541     };
2542 }