View Javadoc

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