View Javadoc

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