View Javadoc

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