View Javadoc

1   /*
2    * $Id: AbstractConnector.java 10724 2008-02-06 15:34:56Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.providers;
12  
13  import org.mule.MuleManager;
14  import org.mule.MuleRuntimeException;
15  import org.mule.config.ThreadingProfile;
16  import org.mule.config.i18n.CoreMessages;
17  import org.mule.impl.AlreadyInitialisedException;
18  import org.mule.impl.DefaultExceptionStrategy;
19  import org.mule.impl.ImmutableMuleEndpoint;
20  import org.mule.impl.MuleSessionHandler;
21  import org.mule.impl.internal.notifications.ConnectionNotification;
22  import org.mule.providers.service.TransportFactory;
23  import org.mule.providers.service.TransportServiceDescriptor;
24  import org.mule.providers.service.TransportServiceException;
25  import org.mule.routing.filters.WildcardFilter;
26  import org.mule.umo.MessagingException;
27  import org.mule.umo.UMOComponent;
28  import org.mule.umo.UMOEvent;
29  import org.mule.umo.UMOException;
30  import org.mule.umo.UMOMessage;
31  import org.mule.umo.endpoint.UMOEndpoint;
32  import org.mule.umo.endpoint.UMOEndpointURI;
33  import org.mule.umo.endpoint.UMOImmutableEndpoint;
34  import org.mule.umo.lifecycle.DisposeException;
35  import org.mule.umo.lifecycle.Initialisable;
36  import org.mule.umo.lifecycle.InitialisationException;
37  import org.mule.umo.manager.UMOServerNotification;
38  import org.mule.umo.manager.UMOWorkManager;
39  import org.mule.umo.provider.ConnectorException;
40  import org.mule.umo.provider.DispatchException;
41  import org.mule.umo.provider.UMOConnectable;
42  import org.mule.umo.provider.UMOConnector;
43  import org.mule.umo.provider.UMOMessageAdapter;
44  import org.mule.umo.provider.UMOMessageDispatcher;
45  import org.mule.umo.provider.UMOMessageDispatcherFactory;
46  import org.mule.umo.provider.UMOMessageReceiver;
47  import org.mule.umo.provider.UMOSessionHandler;
48  import org.mule.umo.provider.UMOStreamMessageAdapter;
49  import org.mule.umo.transformer.UMOTransformer;
50  import org.mule.util.ClassUtils;
51  import org.mule.util.CollectionUtils;
52  import org.mule.util.ObjectNameHelper;
53  import org.mule.util.ObjectUtils;
54  import org.mule.util.PropertiesUtils;
55  import org.mule.util.StringUtils;
56  import org.mule.util.concurrent.NamedThreadFactory;
57  import org.mule.util.concurrent.WaitableBoolean;
58  
59  import java.beans.ExceptionListener;
60  import java.io.InputStream;
61  import java.io.OutputStream;
62  import java.util.ArrayList;
63  import java.util.Collections;
64  import java.util.HashMap;
65  import java.util.Iterator;
66  import java.util.List;
67  import java.util.Map;
68  import java.util.Properties;
69  
70  import javax.resource.spi.work.WorkEvent;
71  import javax.resource.spi.work.WorkListener;
72  
73  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
74  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
75  import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService;
76  import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor;
77  import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory;
78  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
79  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
80  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicReference;
81  
82  import org.apache.commons.beanutils.BeanUtils;
83  import org.apache.commons.logging.Log;
84  import org.apache.commons.logging.LogFactory;
85  import org.apache.commons.pool.KeyedPoolableObjectFactory;
86  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
87  
88  /**
89   * <code>AbstractConnector</code> provides base functionality for all connectors
90   * provided with Mule. Connectors are the mechanism used to connect to external
91   * systems and protocols in order to send and receive data. <p/> The
92   * <code>AbstractConnector</code> provides getter and setter methods for endpoint
93   * name, transport name and protocol. It also provides methods to stop and start
94   * connecotors and sets up a dispatcher threadpool which allows deriving connectors
95   * the possibility to dispatch work to separate threads. This functionality is
96   * controlled with the <i> doThreading</i> property on the threadingProfiles for
97   * dispachers and receivers. The lifecycle for a connector is -
98   * <ol>
99   * <li>Create
100  * <li>Initialise
101  * <li>Connect
102  * <li>Connect receivers
103  * <li>Start
104  * <li>Start Receivers
105  * <li>Stop
106  * <li>Stop Receivers
107  * <li>Disconnect
108  * <li>Disconnect Receivers
109  * <li>Dispose
110  * <li>Dispose Receivers
111  * </ol>
112  */
113 public abstract class AbstractConnector
114     implements UMOConnector, ExceptionListener, UMOConnectable, WorkListener
115 {
116     /**
117      * Default number of concurrent transactional receivers.
118      */
119     public static final int DEFAULT_NUM_CONCURRENT_TX_RECEIVERS = 4;
120 
121     /**
122      * logger used by this class
123      */
124     protected final Log logger = LogFactory.getLog(getClass());
125 
126     /**
127      * Specifies if the endpoint started
128      */
129     protected final AtomicBoolean started = new AtomicBoolean(false);
130 
131     /**
132      * True once the endpoint has been initialsed
133      */
134     protected final AtomicBoolean initialised = new AtomicBoolean(false);
135 
136     /**
137      * The name that identifies the endpoint
138      */
139     protected volatile String name;
140 
141     /**
142      * The exception strategy used by this connector
143      */
144     protected volatile ExceptionListener exceptionListener;
145 
146     /**
147      * Determines in the connector is alive and well
148      */
149     protected final AtomicBoolean disposed = new AtomicBoolean(false);
150 
151     /**
152      * Determines in connector has been told to dispose
153      */
154     protected final AtomicBoolean disposing = new AtomicBoolean(false);
155 
156     /**
157      * Factory used to create dispatchers for this connector
158      */
159     protected volatile UMOMessageDispatcherFactory dispatcherFactory;
160 
161     /**
162      * A pool of dispatchers for this connector, keyed by endpoint
163      */
164     protected final GenericKeyedObjectPool dispatchers = new GenericKeyedObjectPool();
165 
166     /**
167      * The collection of listeners on this connector. Keyed by entrypoint
168      */
169     protected final ConcurrentMap receivers = new ConcurrentHashMap();
170 
171     /**
172      * Defines the dispatcher threading profile
173      */
174     private volatile ThreadingProfile dispatcherThreadingProfile = MuleManager.getConfiguration()
175         .getMessageDispatcherThreadingProfile();
176 
177     /**
178      * Defines the receiver threading profile
179      */
180     private volatile ThreadingProfile receiverThreadingProfile = MuleManager.getConfiguration()
181         .getMessageReceiverThreadingProfile();
182 
183     /**
184      * @see {@link #isCreateMultipleTransactedReceivers()}
185      */
186     protected volatile boolean createMultipleTransactedReceivers = true;
187 
188     /**
189      * @see {@link #getNumberOfConcurrentTransactedReceivers()}
190      */
191     protected volatile int numberOfConcurrentTransactedReceivers = DEFAULT_NUM_CONCURRENT_TX_RECEIVERS;
192 
193     /**
194      * The service descriptor can define a default inbound transformer to be used on
195      * an endpoint if no other is set
196      */
197     protected volatile UMOTransformer defaultInboundTransformer;
198 
199     /**
200      * The service descriptor can define a default outbound transformer to be used on
201      * an endpoint if no other is set
202      */
203     protected volatile UMOTransformer defaultOutboundTransformer;
204 
205     /**
206      * For some connectors such as http, a response transformer is required or where
207      * a replyTo needs a trnasformer
208      */
209     protected volatile UMOTransformer defaultResponseTransformer;
210 
211     protected volatile ConnectionStrategy connectionStrategy;
212 
213     protected final WaitableBoolean connected = new WaitableBoolean(false);
214 
215     protected final WaitableBoolean connecting = new WaitableBoolean(false);
216 
217     /**
218      * If the connect method was called via the start method, this will be set so
219      * that when the connector comes on line it will be started
220      */
221     protected final WaitableBoolean startOnConnect = new WaitableBoolean(false);
222 
223     /**
224      * Whether to fire message notifications for every message that is sent or
225      * received from this connector. Default is {@code false}.
226      */
227     private volatile boolean enableMessageEvents;
228 
229     private final List supportedProtocols;
230 
231     /**
232      * A shared work manager for all receivers registered with this connector.
233      */
234     private final AtomicReference/*<UMOWorkManager>*/ receiverWorkManager = new AtomicReference();
235 
236     /**
237      * A shared work manager for all dispatchers created for this connector.
238      */
239     private final AtomicReference/*<UMOWorkManager>*/ dispatcherWorkManager = new AtomicReference();
240 
241     /**
242      * A generic scheduling service for tasks that need to be performed periodically.
243      */
244     private final AtomicReference/*<ScheduledExecutorService>*/ scheduler = new AtomicReference();
245 
246     /**
247      * Holds the service configuration for this connector
248      */
249     protected volatile TransportServiceDescriptor serviceDescriptor;
250 
251     /**
252      * The map of service overrides that can e used to extend the capabilities of the
253      * connector
254      */
255     protected volatile Properties serviceOverrides;
256 
257     /**
258      * The strategy used for reading and writing session information to and fromt he
259      * transport
260      */
261     protected volatile UMOSessionHandler sessionHandler = new MuleSessionHandler();
262 
263     /** Constructs a new AbstractConnector. */
264     public AbstractConnector()
265     {
266         super();
267 
268         // make sure we always have an exception strategy
269         exceptionListener = new DefaultExceptionStrategy();
270         connectionStrategy = MuleManager.getConfiguration().getConnectionStrategy();
271         enableMessageEvents = MuleManager.getConfiguration().isEnableMessageEvents();
272 
273         // always add at least the default protocol
274         supportedProtocols = new ArrayList();
275         supportedProtocols.add(getProtocol().toLowerCase());
276 
277         // NOTE: testOnBorrow MUST be FALSE. this is a bit of a design bug in
278         // commons-pool since validate is used for both activation and passivation,
279         // but has no way of knowing which way it is going.
280         dispatchers.setTestOnBorrow(false);
281         dispatchers.setTestOnReturn(true);
282     }
283 
284     /** {@inheritDoc} */
285     public String getName()
286     {
287         return name;
288     }
289 
290     /** {@inheritDoc} */
291     public void setName(String newName)
292     {
293         if (newName == null)
294         {
295             throw new IllegalArgumentException(CoreMessages.objectIsNull("Connector name").toString());
296         }
297 
298         if (logger.isDebugEnabled())
299         {
300             logger.debug("Set UMOConnector name to: " + newName);
301         }
302 
303         name = newName;
304     }
305 
306     /** {@inheritDoc} */
307     public final synchronized void initialise() throws InitialisationException
308     {
309         if (initialised.get())
310         {
311             throw new AlreadyInitialisedException("Connector '" + getName() + "'", this);
312         }
313 
314         if (logger.isInfoEnabled())
315         {
316             logger.info("Initialising: " + this);
317         }
318 
319         // Initialise the structure of this connector
320         this.initFromServiceDescriptor();
321 
322         // we clear out any registered dispatchers and receivers without resetting
323         // the actual containers since this it might actually be a re-initialise
324         // (e.g. as in JmsConnector)
325         this.disposeDispatchers();
326         this.disposeReceivers();
327 
328         this.doInitialise();
329 
330         if (exceptionListener instanceof Initialisable)
331         {
332             ((Initialisable) exceptionListener).initialise();
333         }
334 
335         initialised.set(true);
336     }
337 
338     /** {@inheritDoc} */
339     public final synchronized void startConnector() throws UMOException
340     {
341         this.checkDisposed();
342 
343         if (!this.isStarted())
344         {
345             if (!this.isConnected())
346             {
347                 startOnConnect.set(true);
348                 // Don't call getConnectionStrategy(), it clones the connection strategy.
349                 // Connectors should have a single reconnection thread, unlike per receiver/dispatcher
350                 connectionStrategy.connect(this);
351                 // Only start once we are connected
352                 return;
353             }
354 
355             if (logger.isInfoEnabled())
356             {
357                 logger.info("Starting: " + this);
358             }
359 
360             // the scheduler is recreated after stopConnector()
361             ScheduledExecutorService currentScheduler = (ScheduledExecutorService) scheduler.get();
362             if (currentScheduler == null || currentScheduler.isShutdown())
363             {
364                 scheduler.set(this.getScheduler());
365             }
366 
367             this.doStart();
368             started.set(true);
369 
370             if (receivers != null)
371             {
372                 for (Iterator iterator = receivers.values().iterator(); iterator.hasNext();)
373                 {
374                     UMOMessageReceiver mr = (UMOMessageReceiver) iterator.next();
375                     if (logger.isDebugEnabled())
376                     {
377                         logger.debug("Starting receiver on endpoint: " + mr.getEndpoint().getEndpointURI());
378                     }
379                     mr.start();
380                 }
381             }
382 
383             if (logger.isInfoEnabled())
384             {
385                 logger.info("Started: " + this);
386             }
387         }
388     }
389 
390     /** {@inheritDoc} */
391     public boolean isStarted()
392     {
393         return started.get();
394     }
395 
396     /** {@inheritDoc} */
397     public final synchronized void stopConnector() throws UMOException
398     {
399         if (this.isDisposed())
400         {
401             return;
402         }
403 
404         if (this.isStarted())
405         {
406             if (logger.isInfoEnabled())
407             {
408                 logger.info("Stopping: " + this);
409             }
410 
411             // shutdown our scheduler service
412             ((ScheduledExecutorService) scheduler.get()).shutdown();
413 
414             this.doStop();
415             started.set(false);
416 
417             // Stop all the receivers on this connector (this will cause them to
418             // disconnect too)
419             if (receivers != null)
420             {
421                 for (Iterator iterator = receivers.values().iterator(); iterator.hasNext();)
422                 {
423                     UMOMessageReceiver mr = (UMOMessageReceiver) iterator.next();
424                     if (logger.isDebugEnabled())
425                     {
426                         logger.debug("Stopping receiver on endpoint: " + mr.getEndpoint().getEndpointURI());
427                     }
428                     mr.stop();
429                 }
430             }
431         }
432         
433         if (this.isConnected())
434         {
435             try
436             {
437                 this.disconnect();
438             }
439             catch (Exception e)
440             {
441                 // TODO MULE-863: What should we really do?
442                 logger.error("Failed to disconnect: " + e.getMessage(), e);
443             }
444         }
445 
446         // make sure the scheduler is gone
447         scheduler.set(null);
448 
449         // we do not need to stop the work managers because they do no harm (will just be idle)
450         // and will be reused on restart without problems.
451         
452         this.initialised.set(false);
453         // started=false already issued above right after doStop()
454         if (logger.isInfoEnabled())
455         {
456             logger.info("Stopped: " + this);
457         }
458     }
459 
460     /** {@inheritDoc} */
461     public final synchronized void dispose()
462     {
463         disposing.set(true);
464 
465         if (logger.isInfoEnabled())
466         {
467             logger.info("Disposing: " + this);
468         }
469 
470         try
471         {
472             this.stopConnector();
473         }
474         catch (UMOException e)
475         {
476             logger.warn("Failed to stop during shutdown: " + e.getMessage(), e);
477         }
478 
479         this.disposeReceivers();
480         this.disposeDispatchers();
481         this.disposeWorkManagers();
482 
483         this.doDispose();        
484         disposed.set(true);
485 
486         if (logger.isInfoEnabled())
487         {
488             logger.info("Disposed: " + this);
489         }
490     }
491 
492     protected void disposeWorkManagers()
493     {
494         logger.debug("Disposing dispatcher work manager");
495         UMOWorkManager workManager = (UMOWorkManager) dispatcherWorkManager.get();
496         if (workManager != null)
497         {
498             workManager.dispose();
499         }
500         dispatcherWorkManager.set(null);
501 
502         logger.debug("Disposing receiver work manager");
503         workManager = (UMOWorkManager) receiverWorkManager.get();
504         if (workManager != null)
505         {
506             workManager.dispose();
507         }
508         receiverWorkManager.set(null);
509     }
510 
511     protected void disposeReceivers()
512     {
513         if (receivers != null)
514         {
515             logger.debug("Disposing Receivers");
516 
517             for (Iterator iterator = receivers.values().iterator(); iterator.hasNext();)
518             {
519                 UMOMessageReceiver receiver = (UMOMessageReceiver) iterator.next();
520 
521                 try
522                 {
523                     this.destroyReceiver(receiver, receiver.getEndpoint());
524                 }
525                 catch (Throwable e)
526                 {
527                     // TODO MULE-863: What should we really do?
528                     logger.error("Failed to destroy receiver: " + receiver, e);
529                 }
530             }
531 
532             receivers.clear();
533             logger.debug("Receivers Disposed");
534         }
535     }
536 
537     protected void disposeDispatchers()
538     {
539         if (dispatchers != null)
540         {
541             logger.debug("Disposing Dispatchers");
542             dispatchers.clear();
543             logger.debug("Dispatchers Disposed");
544         }
545     }
546 
547     /** {@inheritDoc} */
548     public boolean isDisposed()
549     {
550         return disposed.get();
551     }
552 
553     /** {@inheritDoc} */
554     public void handleException(Exception exception)
555     {
556         if (exceptionListener == null)
557         {
558             throw new MuleRuntimeException(
559                 CoreMessages.exceptionOnConnectorNotExceptionListener(this.getName()), exception);
560         }
561         else
562         {
563             exceptionListener.exceptionThrown(exception);
564         }
565     }
566 
567     /** {@inheritDoc} */
568     public void exceptionThrown(Exception e)
569     {
570         handleException(e);
571     }
572 
573     /**
574      * @return the ExceptionStrategy for this endpoint
575      * @see ExceptionListener
576      */
577     public ExceptionListener getExceptionListener()
578     {
579         return exceptionListener;
580     }
581 
582     /**
583      * @param listener the ExceptionStrategy to use with this endpoint
584      * @see ExceptionListener
585      */
586     public void setExceptionListener(ExceptionListener listener)
587     {
588         exceptionListener = listener;
589     }
590 
591     /**
592      * @return Returns the dispatcherFactory.
593      */
594     public UMOMessageDispatcherFactory getDispatcherFactory()
595     {
596         return dispatcherFactory;
597     }
598 
599     /**
600      * @param dispatcherFactory The dispatcherFactory to set.
601      */
602     public void setDispatcherFactory(UMOMessageDispatcherFactory dispatcherFactory)
603     {
604         KeyedPoolableObjectFactory poolFactory;
605 
606         if (dispatcherFactory instanceof KeyedPoolableObjectFactory)
607         {
608             poolFactory = (KeyedPoolableObjectFactory) dispatcherFactory;
609         }
610         else
611         {
612             // need to adapt the UMOMessageDispatcherFactory for use by commons-pool
613             poolFactory = new KeyedPoolMessageDispatcherFactoryAdapter(dispatcherFactory);
614         }
615 
616         this.dispatchers.setFactory(poolFactory);
617 
618         // we keep a reference to the unadapted factory, otherwise people might end
619         // up with ClassCastExceptions on downcast to their implementation (sigh)
620         this.dispatcherFactory = dispatcherFactory;
621     }
622 
623     /**
624      * Returns the maximum number of dispatchers that can be concurrently active per
625      * endpoint.
626      *
627      * @return max. number of active dispatchers
628      */
629     public int getMaxDispatchersActive()
630     {
631         return this.dispatchers.getMaxActive();
632     }
633 
634     /**
635      * Configures the maximum number of dispatchers that can be concurrently active
636      * per endpoint
637      *
638      * @param maxActive max. number of active dispatchers
639      */
640     public void setMaxDispatchersActive(int maxActive)
641     {
642         this.dispatchers.setMaxActive(maxActive);
643         // adjust maxIdle in tandem to avoid thrashing
644         this.dispatchers.setMaxIdle(maxActive);
645     }
646 
647     private UMOMessageDispatcher getDispatcher(UMOImmutableEndpoint endpoint) throws UMOException
648     {
649         this.checkDisposed();
650 
651         if (endpoint == null)
652         {
653             throw new IllegalArgumentException("Endpoint must not be null");
654         }
655 
656         if (!this.supportsProtocol(endpoint.getConnector().getProtocol()))
657         {
658             throw new IllegalArgumentException(
659                 CoreMessages.connectorSchemeIncompatibleWithEndpointScheme(this.getProtocol(), 
660                     endpoint.getEndpointURI().toString()).getMessage());
661         }
662 
663         try
664         {
665             if (logger.isDebugEnabled())
666             {
667                 logger.debug("Borrowing a dispatcher for endpoint: " + endpoint.getEndpointURI());
668             }
669         }
670         catch (Exception ex)
671         {
672             throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
673         }
674 
675         UMOMessageDispatcher dispatcher = null;
676 
677         try
678         {
679             dispatcher = (UMOMessageDispatcher) dispatchers.borrowObject(endpoint);
680             return dispatcher;
681         }
682         catch (Exception ex)
683         {
684             throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
685         }
686         finally
687         {
688             try
689             {
690                 if (logger.isDebugEnabled())
691                 {
692                     logger.debug("Borrowed dispatcher: " + ObjectUtils.toString(dispatcher, "null"));
693                 }
694             }
695             catch (Exception ex)
696             {
697                 throw new ConnectorException(CoreMessages.connectorCausedError(), this, ex);
698             }
699         }
700     }
701 
702     private void returnDispatcher(UMOImmutableEndpoint endpoint, UMOMessageDispatcher dispatcher)
703     {
704         if (endpoint != null && dispatcher != null)
705         {
706             try
707             {
708                 if (logger.isDebugEnabled())
709                 {
710                     logger.debug("Returning dispatcher for endpoint: " + endpoint.getEndpointURI() + " = "
711                                     + ObjectUtils.toString(dispatcher, "null"));
712                 }
713             }
714             catch (Exception ex)
715             {
716                 // TODO MULE-863: What should we really do?
717                 // ignore - if the dispatcher is broken, it will likely get cleaned
718                 // up by the factory
719             }
720             finally
721             {
722                 try
723                 {
724                     dispatchers.returnObject(endpoint, dispatcher);
725                 }
726                 catch (Exception ex)
727                 {
728                     // TODO MULE-863: What should we really do?
729                     // returnObject() never throws; this will go away in commons-pool
730                 }
731             }
732         }
733     }
734 
735     protected void checkDisposed() throws DisposeException
736     {
737         if (this.isDisposed())
738         {
739             throw new DisposeException(CoreMessages.cannotUseDisposedConnector(), this);
740         }
741     }
742 
743     /** {@inheritDoc} */
744     public UMOMessageReceiver registerListener(UMOComponent component, UMOEndpoint endpoint) throws Exception
745     {
746         if (endpoint == null)
747         {
748             throw new IllegalArgumentException("The endpoint cannot be null when registering a listener");
749         }
750 
751         if (component == null)
752         {
753             throw new IllegalArgumentException("The component cannot be null when registering a listener");
754         }
755 
756         UMOEndpointURI endpointUri = endpoint.getEndpointURI();
757         if (endpointUri == null)
758         {
759             throw new ConnectorException(CoreMessages.endpointIsNullForListener(), this);
760         }
761 
762         logger.info("Registering listener: " + component.getDescriptor().getName() + " on endpointUri: "
763                         + endpointUri.toString());
764 
765         UMOMessageReceiver receiver = this.getReceiver(component, endpoint);
766 
767         if (receiver != null)
768         {
769             throw new ConnectorException(CoreMessages.listenerAlreadyRegistered(endpointUri), this);
770         }
771         else
772         {
773             receiver = this.createReceiver(component, endpoint);
774             Object receiverKey = getReceiverKey(component, endpoint);
775             receiver.setReceiverKey(receiverKey.toString());
776             receivers.put(receiverKey, receiver);
777             // receivers.put(getReceiverKey(component, endpoint), receiver);
778         }
779 
780         return receiver;
781     }
782 
783     /**
784      * The method determines the key used to store the receiver against.
785      *
786      * @param component the component for which the endpoint is being registered
787      * @param endpoint the endpoint being registered for the component
788      * @return the key to store the newly created receiver against
789      */
790     protected Object getReceiverKey(UMOComponent component, UMOEndpoint endpoint)
791     {
792         return StringUtils.defaultIfEmpty(endpoint.getEndpointURI().getFilterAddress(), endpoint
793             .getEndpointURI().getAddress());
794     }
795 
796     /** {@inheritDoc} */
797     public final void unregisterListener(UMOComponent component, UMOEndpoint endpoint) throws Exception
798     {
799         if (component == null)
800         {
801             throw new IllegalArgumentException(
802                 "The component must not be null when you unregister a listener");
803         }
804 
805         if (endpoint == null)
806         {
807             throw new IllegalArgumentException("The endpoint must not be null when you unregister a listener");
808         }
809 
810         UMOEndpointURI endpointUri = endpoint.getEndpointURI();
811         if (endpointUri == null)
812         {
813             throw new IllegalArgumentException(
814                 "The endpointUri must not be null when you unregister a listener");
815         }
816 
817         if (logger.isInfoEnabled())
818         {
819             logger.info("Removing listener on endpointUri: " + endpointUri);
820         }
821 
822         if (receivers != null && !receivers.isEmpty())
823         {
824             UMOMessageReceiver receiver = (UMOMessageReceiver) receivers.remove(this.getReceiverKey(component,
825                 endpoint));
826             if (receiver != null)
827             {
828                 this.destroyReceiver(receiver, endpoint);
829             }
830         }
831     }
832 
833     /**
834      * Getter for property 'dispatcherThreadingProfile'.
835      *
836      * @return Value for property 'dispatcherThreadingProfile'.
837      */
838     public ThreadingProfile getDispatcherThreadingProfile()
839     {
840         return dispatcherThreadingProfile;
841     }
842 
843     /**
844      * Setter for property 'dispatcherThreadingProfile'.
845      *
846      * @param dispatcherThreadingProfile Value to set for property
847      *            'dispatcherThreadingProfile'.
848      */
849     public void setDispatcherThreadingProfile(ThreadingProfile dispatcherThreadingProfile)
850     {
851         this.dispatcherThreadingProfile = dispatcherThreadingProfile;
852     }
853 
854     /**
855      * Getter for property 'receiverThreadingProfile'.
856      *
857      * @return Value for property 'receiverThreadingProfile'.
858      */
859     public ThreadingProfile getReceiverThreadingProfile()
860     {
861         return receiverThreadingProfile;
862     }
863 
864     /**
865      * Setter for property 'receiverThreadingProfile'.
866      *
867      * @param receiverThreadingProfile Value to set for property
868      *            'receiverThreadingProfile'.
869      */
870     public void setReceiverThreadingProfile(ThreadingProfile receiverThreadingProfile)
871     {
872         this.receiverThreadingProfile = receiverThreadingProfile;
873     }
874 
875     protected void destroyReceiver(UMOMessageReceiver receiver, UMOEndpoint endpoint) throws Exception
876     {
877         receiver.dispose();
878     }
879 
880     protected abstract void doInitialise() throws InitialisationException;
881 
882     /**
883      * Template method to perform any work when destroying the connectoe
884      */
885     protected abstract void doDispose();
886 
887     /**
888      * Template method to perform any work when starting the connectoe
889      *
890      * @throws UMOException if the method fails
891      */
892     protected abstract void doStart() throws UMOException;
893 
894     /**
895      * Template method to perform any work when stopping the connectoe
896      *
897      * @throws UMOException if the method fails
898      */
899     protected abstract void doStop() throws UMOException;
900 
901     /**
902      * Getter for property 'defaultInboundTransformer'.
903      *
904      * @return Value for property 'defaultInboundTransformer'.
905      */
906     public UMOTransformer getDefaultInboundTransformer()
907     {
908         if (defaultInboundTransformer != null)
909         {
910             try
911             {
912                 return (UMOTransformer) defaultInboundTransformer.clone();
913             }
914             catch (CloneNotSupportedException e)
915             {
916                 // TODO MULE-863: What should we really do?
917                 logger.error("Failed to clone default Inbound transformer");
918             }
919         }
920 
921         return null;
922     }
923 
924     /**
925      * Setter for property 'defaultInboundTransformer'.
926      *
927      * @param defaultInboundTransformer Value to set for property
928      *            'defaultInboundTransformer'.
929      */
930     public void setDefaultInboundTransformer(UMOTransformer defaultInboundTransformer)
931     {
932         this.defaultInboundTransformer = defaultInboundTransformer;
933     }
934 
935     /**
936      * Getter for property 'defaultResponseTransformer'.
937      *
938      * @return Value for property 'defaultResponseTransformer'.
939      */
940     public UMOTransformer getDefaultResponseTransformer()
941     {
942         if (defaultResponseTransformer != null)
943         {
944             try
945             {
946                 return (UMOTransformer) defaultResponseTransformer.clone();
947             }
948             catch (CloneNotSupportedException e)
949             {
950                 // TODO MULE-863: What should we really do?
951                 logger.error("Failed to clone default Outbound transformer");
952             }
953         }
954 
955         return null;
956     }
957 
958     /**
959      * Getter for property 'defaultOutboundTransformer'.
960      *
961      * @return Value for property 'defaultOutboundTransformer'.
962      */
963     public UMOTransformer getDefaultOutboundTransformer()
964     {
965         if (defaultOutboundTransformer != null)
966         {
967             try
968             {
969                 return (UMOTransformer) defaultOutboundTransformer.clone();
970             }
971             catch (CloneNotSupportedException e)
972             {
973                 // TODO MULE-863: What should we really do?
974                 logger.error("Failed to clone default Outbound transformer");
975             }
976         }
977 
978         return null;
979     }
980 
981     /**
982      * Setter for property 'defaultOutboundTransformer'.
983      *
984      * @param defaultOutboundTransformer Value to set for property
985      *            'defaultOutboundTransformer'.
986      */
987     public void setDefaultOutboundTransformer(UMOTransformer defaultOutboundTransformer)
988     {
989         this.defaultOutboundTransformer = defaultOutboundTransformer;
990     }
991 
992     /**
993      * Setter for property 'defaultResponseTransformer'.
994      *
995      * @param defaultResponseTransformer Value to set for property
996      *            'defaultResponseTransformer'.
997      */
998     public void setDefaultResponseTransformer(UMOTransformer defaultResponseTransformer)
999     {
1000         this.defaultResponseTransformer = defaultResponseTransformer;
1001     }
1002 
1003     /**
1004      * Getter for property 'replyToHandler'.
1005      *
1006      * @return Value for property 'replyToHandler'.
1007      */
1008     public ReplyToHandler getReplyToHandler()
1009     {
1010         return new DefaultReplyToHandler(defaultResponseTransformer);
1011     }
1012 
1013     /**
1014      * Fires a server notification to all registered
1015      * {@link org.mule.impl.internal.notifications.CustomNotificationListener}
1016      * eventManager.
1017      *
1018      * @param notification the notification to fire. This must be of type
1019      *            {@link org.mule.impl.internal.notifications.CustomNotification}
1020      *            otherwise an exception will be thrown.
1021      * @throws UnsupportedOperationException if the notification fired is not a
1022      *             {@link org.mule.impl.internal.notifications.CustomNotification}
1023      */
1024     public void fireNotification(UMOServerNotification notification)
1025     {
1026         MuleManager.getInstance().fireNotification(notification);
1027     }
1028 
1029     /**
1030      * Getter for property 'connectionStrategy'.
1031      *
1032      * @return Value for property 'connectionStrategy'.
1033      */
1034     public ConnectionStrategy getConnectionStrategy()
1035     {
1036         // not happy with this but each receiver needs its own instance
1037         // of the connection strategy and using a factory just introduces extra
1038         // implementation
1039         try
1040         {
1041             return (ConnectionStrategy) BeanUtils.cloneBean(connectionStrategy);
1042         }
1043         catch (Exception e)
1044         {
1045             throw new MuleRuntimeException(CoreMessages.failedToClone("connectionStrategy"), e);
1046         }
1047     }
1048 
1049     /**
1050      * Setter for property 'connectionStrategy'.
1051      *
1052      * @param connectionStrategy Value to set for property 'connectionStrategy'.
1053      */
1054     public void setConnectionStrategy(ConnectionStrategy connectionStrategy)
1055     {
1056         this.connectionStrategy = connectionStrategy;
1057     }
1058 
1059     /** {@inheritDoc} */
1060     public boolean isDisposing()
1061     {
1062         return disposing.get();
1063     }
1064 
1065     /** {@inheritDoc} */
1066     public boolean isRemoteSyncEnabled()
1067     {
1068         return false;
1069     }
1070 
1071     public UMOMessageReceiver getReceiver(UMOComponent component, UMOEndpoint endpoint)
1072     {
1073         return (UMOMessageReceiver) receivers.get(this.getReceiverKey(component, endpoint));
1074     }
1075 
1076     /**
1077      * Getter for property 'receivers'.
1078      *
1079      * @return Value for property 'receivers'.
1080      */
1081     public Map getReceivers()
1082     {
1083         return Collections.unmodifiableMap(receivers);
1084     }
1085 
1086     public UMOMessageReceiver lookupReceiver(String key)
1087     {
1088         if (key != null)
1089         {
1090             return (UMOMessageReceiver) receivers.get(key);
1091         }
1092         else
1093         {
1094             throw new IllegalArgumentException("Receiver key must not be null");
1095         }
1096     }
1097 
1098     public UMOMessageReceiver[] getReceivers(String wildcardExpression)
1099     {
1100         WildcardFilter filter = new WildcardFilter(wildcardExpression);
1101         filter.setCaseSensitive(false);
1102 
1103         List found = new ArrayList();
1104 
1105         for (Iterator iterator = receivers.entrySet().iterator(); iterator.hasNext();)
1106         {
1107             Map.Entry e = (Map.Entry) iterator.next();
1108             if (filter.accept(e.getKey()))
1109             {
1110                 found.add(e.getValue());
1111             }
1112         }
1113 
1114         return (UMOMessageReceiver[]) CollectionUtils.toArrayOfComponentType(found,
1115             UMOMessageReceiver.class);
1116     }
1117 
1118     /** {@inheritDoc} */
1119     public void connect() throws Exception
1120     {
1121         this.checkDisposed();
1122 
1123         if (connected.get())
1124         {
1125             return;
1126         }
1127 
1128         /*
1129             Until the recursive startConnector() -> connect() -> doConnect() -> connect()
1130             calls are unwound between a connector and connection strategy, this call has
1131             to be here, and not below (commented out currently). Otherwise, e.g. WebspherMQ
1132             goes into an endless reconnect thrashing loop, see MULE-1150 for more details.
1133         */
1134         try
1135         {
1136             if (connecting.get())
1137             {
1138                 this.doConnect();
1139             }
1140 
1141             if (connecting.compareAndSet(false, true))
1142             {
1143                 if (logger.isDebugEnabled())
1144                 {
1145                     logger.debug("Connecting: " + this);
1146                 }
1147 
1148                 connectionStrategy.connect(this);
1149 
1150                 logger.info("Connected: " + getConnectionDescription());
1151                 // This method calls itself so the connecting flag is set first, then
1152                 // the connection is made on the second call
1153                 return;
1154             }
1155 
1156 
1157             // see the explanation above
1158             //this.doConnect();
1159             connected.set(true);
1160             connecting.set(false);
1161 
1162             this.fireNotification(new ConnectionNotification(this, getConnectEventId(),
1163                 ConnectionNotification.CONNECTION_CONNECTED));
1164         }
1165         catch (Exception e)
1166         {
1167             connected.set(false);
1168             connecting.set(false);
1169 
1170             this.fireNotification(new ConnectionNotification(this, getConnectEventId(),
1171                 ConnectionNotification.CONNECTION_FAILED));
1172 
1173             if (e instanceof ConnectException || e instanceof FatalConnectException)
1174             {
1175                 // rethrow
1176                 throw e;
1177             }
1178             else
1179             {
1180                 throw new ConnectException(e, this);
1181             }
1182         }
1183 
1184         if (startOnConnect.get())
1185         {
1186             this.startConnector();
1187         }
1188         else
1189         {
1190             for (Iterator iterator = receivers.values().iterator(); iterator.hasNext();)
1191             {
1192                 UMOMessageReceiver receiver = (UMOMessageReceiver) iterator.next();
1193                 if (logger.isDebugEnabled())
1194                 {
1195                     logger.debug("Connecting receiver on endpoint: "
1196                                     + receiver.getEndpoint().getEndpointURI());
1197                 }
1198                 receiver.connect();
1199             }
1200         }
1201     }
1202 
1203     /** {@inheritDoc} */
1204     public void disconnect() throws Exception
1205     {
1206         startOnConnect.set(this.isStarted());
1207 
1208         this.fireNotification(new ConnectionNotification(this, getConnectEventId(),
1209             ConnectionNotification.CONNECTION_DISCONNECTED));
1210 
1211         connected.set(false);
1212 
1213         try
1214         {
1215             this.doDisconnect();
1216         }
1217         finally
1218         {
1219             this.stopConnector();
1220         }
1221 
1222         logger.info("Disconnected: " + this.getConnectionDescription());
1223     }
1224 
1225     /** {@inheritDoc} */
1226     public String getConnectionDescription()
1227     {
1228         return this.toString();
1229     }
1230 
1231     /** {@inheritDoc} */
1232     public final boolean isConnected()
1233     {
1234         return connected.get();
1235     }
1236 
1237     /**
1238      * Template method where any connections should be made for the connector
1239      *
1240      * @throws Exception
1241      */
1242     protected abstract void doConnect() throws Exception;
1243 
1244     /**
1245      * Template method where any connected resources used by the connector should be
1246      * disconnected
1247      *
1248      * @throws Exception
1249      */
1250     protected abstract void doDisconnect() throws Exception;
1251 
1252     /**
1253      * The resource id used when firing ConnectEvents from this connector
1254      *
1255      * @return the resource id used when firing ConnectEvents from this connector
1256      */
1257     protected String getConnectEventId()
1258     {
1259         return getName();
1260     }
1261 
1262     /**
1263      * For better throughput when using TransactedMessageReceivers this will enable a
1264      * number of concurrent receivers, based on the value returned by
1265      * {@link #getNumberOfConcurrentTransactedReceivers()}. This property is used by
1266      * transports that support transactions, specifically receivers that extend the
1267      * TransactedPollingMessageReceiver.
1268      *
1269      * @return true if multiple receivers will be enabled for this connection
1270      */
1271     public boolean isCreateMultipleTransactedReceivers()
1272     {
1273         return createMultipleTransactedReceivers;
1274     }
1275 
1276     /**
1277      * @see {@link #isCreateMultipleTransactedReceivers()}
1278      * @param createMultipleTransactedReceivers if true, multiple receivers will be
1279      *            created for this connection
1280      */
1281     public void setCreateMultipleTransactedReceivers(boolean createMultipleTransactedReceivers)
1282     {
1283         this.createMultipleTransactedReceivers = createMultipleTransactedReceivers;
1284     }
1285 
1286     /**
1287      * Returns the number of concurrent receivers that will be launched when
1288      * {@link #isCreateMultipleTransactedReceivers()} returns <code>true</code>.
1289      *
1290      * @see #DEFAULT_NUM_CONCURRENT_TX_RECEIVERS
1291      */
1292     public int getNumberOfConcurrentTransactedReceivers()
1293     {
1294         return numberOfConcurrentTransactedReceivers;
1295     }
1296 
1297     /**
1298      * @see {@link #getNumberOfConcurrentTransactedReceivers()}
1299      * @param count the number of concurrent transacted receivers to start
1300      */
1301     public void setNumberOfConcurrentTransactedReceivers(int count)
1302     {
1303         numberOfConcurrentTransactedReceivers = count;
1304     }
1305 
1306     /**
1307      * Whether to fire message notifications for every message that is sent or
1308      * received from this connector
1309      */
1310     public boolean isEnableMessageEvents()
1311     {
1312         return enableMessageEvents;
1313     }
1314 
1315     /**
1316      * Whether to fire message notifications for every message that is sent or
1317      * received from this connector
1318      *
1319      * @param enableMessageEvents
1320      */
1321     public void setEnableMessageEvents(boolean enableMessageEvents)
1322     {
1323         this.enableMessageEvents = enableMessageEvents;
1324     }
1325 
1326     /**
1327      * Registers other protocols 'understood' by this connector. These must contain
1328      * scheme meta info. Any protocol registered must begin with the protocol of this
1329      * connector, i.e. If the connector is axis the protocol for jms over axis will
1330      * be axis:jms. Here, 'axis' is the scheme meta info and 'jms' is the protocol.
1331      * If the protocol argument does not start with the connector's protocol, it will
1332      * be appended.
1333      *
1334      * @param protocol the supported protocol to register
1335      */
1336     public void registerSupportedProtocol(String protocol)
1337     {
1338         protocol = protocol.toLowerCase();
1339         if (protocol.startsWith(getProtocol().toLowerCase()))
1340         {
1341             registerSupportedProtocolWithoutPrefix(protocol);
1342         }
1343         else
1344         {
1345             supportedProtocols.add(getProtocol().toLowerCase() + ":" + protocol);
1346         }
1347     }
1348 
1349     /**
1350      * Registers other protocols 'understood' by this connector. These must contain
1351      * scheme meta info. Unlike the {@link #registerSupportedProtocol(String)} method,
1352      * this allows you to register protocols that are not prefixed with the connector
1353      * protocol. This is useful where you use a Service Finder to discover which
1354      * Transport implementation to use. For example the 'wsdl' transport is a generic
1355      * 'finder' transport that will use Axis, Xfire or Glue to create the WSDL
1356      * client. These transport protocols would be wsdl-axis, wsdl-xfire and
1357      * wsdl-glue, but they can all support 'wsdl' protocol too.
1358      *
1359      * @param protocol the supported protocol to register
1360      */
1361     protected void registerSupportedProtocolWithoutPrefix(String protocol)
1362     {
1363         supportedProtocols.add(protocol.toLowerCase());
1364     }
1365 
1366     public void unregisterSupportedProtocol(String protocol)
1367     {
1368         protocol = protocol.toLowerCase();
1369         if (protocol.startsWith(getProtocol().toLowerCase()))
1370         {
1371             supportedProtocols.remove(protocol);
1372         }
1373         else
1374         {
1375             supportedProtocols.remove(getProtocol().toLowerCase() + ":" + protocol);
1376         }
1377     }
1378 
1379     /**
1380      * @return true if the protocol is supported by this connector.
1381      */
1382     public boolean supportsProtocol(String protocol)
1383     {
1384         return supportedProtocols.contains(protocol.toLowerCase());
1385     }
1386 
1387     /**
1388      * Returns an unmodifiable list of the protocols supported by this connector
1389      *
1390      * @return an unmodifiable list of the protocols supported by this connector
1391      */
1392     public List getSupportedProtocols()
1393     {
1394         return Collections.unmodifiableList(supportedProtocols);
1395     }
1396 
1397     /**
1398      * Sets A list of protocols that the connector can accept
1399      *
1400      * @param supportedProtocols
1401      */
1402     public void setSupportedProtocols(List supportedProtocols)
1403     {
1404         for (Iterator iterator = supportedProtocols.iterator(); iterator.hasNext();)
1405         {
1406             String s = (String) iterator.next();
1407             registerSupportedProtocol(s);
1408         }
1409     }
1410 
1411     /**
1412      * Returns a work manager for message receivers.
1413      */
1414     protected UMOWorkManager getReceiverWorkManager(String receiverName) throws UMOException
1415     {
1416         // lazily created because ThreadingProfile was not yet set in Constructor
1417         if (receiverWorkManager.get() == null)
1418         {
1419             UMOWorkManager newWorkManager = this.getReceiverThreadingProfile().createWorkManager(
1420                 this.getName() + '.' + receiverName);
1421 
1422             if (receiverWorkManager.compareAndSet(null, newWorkManager))
1423             {
1424                 newWorkManager.start();
1425             }
1426         }
1427 
1428         return (UMOWorkManager) receiverWorkManager.get();
1429     }
1430 
1431     /**
1432      * Returns a work manager for message dispatchers.
1433      *
1434      * @throws UMOException in case of error
1435      */
1436     protected UMOWorkManager getDispatcherWorkManager() throws UMOException
1437     {
1438         // lazily created because ThreadingProfile was not yet set in Constructor
1439         if (dispatcherWorkManager.get() == null)
1440         {
1441             UMOWorkManager newWorkManager = this.getDispatcherThreadingProfile().createWorkManager(
1442                 getName() + ".dispatcher");
1443 
1444             if (dispatcherWorkManager.compareAndSet(null, newWorkManager))
1445             {
1446                 newWorkManager.start();
1447             }
1448         }
1449 
1450         return (UMOWorkManager) dispatcherWorkManager.get();
1451     }
1452 
1453     /** {@inheritDoc} */
1454     public ScheduledExecutorService getScheduler()
1455     {
1456         if (scheduler.get() == null)
1457         {
1458             ThreadFactory threadFactory = new NamedThreadFactory(this.getName() + ".scheduler");
1459             ScheduledThreadPoolExecutor newExecutor = new ScheduledThreadPoolExecutor(4, threadFactory);
1460             newExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
1461             newExecutor.setKeepAliveTime(this.getReceiverThreadingProfile().getThreadTTL(),
1462                 TimeUnit.MILLISECONDS);
1463             newExecutor.allowCoreThreadTimeOut(true);
1464 
1465             if (!scheduler.compareAndSet(null, newExecutor))
1466             {
1467                 // someone else was faster, ditch our copy.
1468                 newExecutor.shutdown();
1469             }
1470         }
1471 
1472         return (ScheduledExecutorService) scheduler.get();
1473     }
1474 
1475     /**
1476      * Getter for property 'sessionHandler'.
1477      *
1478      * @return Value for property 'sessionHandler'.
1479      */
1480     public UMOSessionHandler getSessionHandler()
1481     {
1482         return sessionHandler;
1483     }
1484 
1485     /**
1486      * Setter for property 'sessionHandler'.
1487      *
1488      * @param sessionHandler Value to set for property 'sessionHandler'.
1489      */
1490     public void setSessionHandler(UMOSessionHandler sessionHandler)
1491     {
1492         this.sessionHandler = sessionHandler;
1493     }
1494 
1495     /** {@inheritDoc} */
1496     public void workAccepted(WorkEvent event)
1497     {
1498         this.handleWorkException(event, "workAccepted");
1499     }
1500 
1501     /** {@inheritDoc} */
1502     public void workRejected(WorkEvent event)
1503     {
1504         this.handleWorkException(event, "workRejected");
1505     }
1506 
1507     /** {@inheritDoc} */
1508     public void workStarted(WorkEvent event)
1509     {
1510         this.handleWorkException(event, "workStarted");
1511     }
1512 
1513     /** {@inheritDoc} */
1514     public void workCompleted(WorkEvent event)
1515     {
1516         this.handleWorkException(event, "workCompleted");
1517     }
1518 
1519     protected void handleWorkException(WorkEvent event, String type)
1520     {
1521         if (event == null)
1522         {
1523             return;
1524         }
1525 
1526         Throwable e = event.getException();
1527 
1528         if (e == null)
1529         {
1530             return;
1531         }
1532 
1533         if (e.getCause() != null)
1534         {
1535             e = e.getCause();
1536         }
1537 
1538         logger.error("Work caused exception on '" + type + "'. Work being executed was: "
1539                         + event.getWork().toString());
1540 
1541         if (e instanceof Exception)
1542         {
1543             this.handleException((Exception) e);
1544         }
1545         else
1546         {
1547             throw new MuleRuntimeException(CoreMessages.connectorCausedError(this.getName()), e);
1548         }
1549     }
1550 
1551     // TODO the following methods should probably be lifecycle-enabled;
1552     // for now they are only stubs to get the refactoring going.
1553 
1554     /** {@inheritDoc} */
1555     public void dispatch(UMOImmutableEndpoint endpoint, UMOEvent event) throws DispatchException
1556     {
1557         UMOMessageDispatcher dispatcher = null;
1558 
1559         try
1560         {
1561             dispatcher = this.getDispatcher(endpoint);
1562             dispatcher.dispatch(event);
1563         }
1564         catch (DispatchException dex)
1565         {
1566             throw dex;
1567         }
1568         catch (UMOException ex)
1569         {
1570             throw new DispatchException(event.getMessage(), endpoint, ex);
1571         }
1572         finally
1573         {
1574             this.returnDispatcher(endpoint, dispatcher);
1575         }
1576     }
1577 
1578     /** {@inheritDoc} */
1579     public UMOMessage receive(UMOEndpointURI endpointUri, long timeout) throws Exception
1580     {
1581         return this.receive(new ImmutableMuleEndpoint(endpointUri.toString(), true), timeout);
1582     }
1583 
1584     /** {@inheritDoc} */
1585     public UMOMessage receive(UMOImmutableEndpoint endpoint, long timeout) throws Exception
1586     {
1587         UMOMessageDispatcher dispatcher = null;
1588 
1589         try
1590         {
1591             dispatcher = this.getDispatcher(endpoint);
1592             return dispatcher.receive(timeout);
1593         }
1594         finally
1595         {
1596             this.returnDispatcher(endpoint, dispatcher);
1597         }
1598     }
1599 
1600     /** {@inheritDoc} */
1601     public UMOMessage send(UMOImmutableEndpoint endpoint, UMOEvent event) throws DispatchException
1602     {
1603         UMOMessageDispatcher dispatcher = null;
1604 
1605         try
1606         {
1607             dispatcher = this.getDispatcher(endpoint);
1608             return dispatcher.send(event);
1609         }
1610         catch (DispatchException dex)
1611         {
1612             throw dex;
1613         }
1614         catch (UMOException ex)
1615         {
1616             throw new DispatchException(event.getMessage(), endpoint, ex);
1617         }
1618         finally
1619         {
1620             this.returnDispatcher(endpoint, dispatcher);
1621         }
1622     }
1623 
1624     // -------- Methods from the removed AbstractServiceEnabled Connector
1625 
1626     /**
1627      * When this connector is created via the
1628      * {@link org.mule.providers.service.TransportFactory} the endpoint used to
1629      * determine the connector type is passed to this method so that any properties
1630      * set on the endpoint that can be used to initialise the connector are made
1631      * available.
1632      *
1633      * @param endpointUri the {@link UMOEndpointURI} use to create this connector
1634      * @throws InitialisationException If there are any problems with the
1635      *             configuration set on the Endpoint or if another exception is
1636      *             thrown it is wrapped in an InitialisationException.
1637      */
1638     public void initialiseFromUrl(UMOEndpointURI endpointUri) throws InitialisationException
1639     {
1640         if (!supportsProtocol(endpointUri.getFullScheme()))
1641         {
1642             throw new InitialisationException(
1643                 CoreMessages.schemeNotCompatibleWithConnector(endpointUri.getFullScheme(), 
1644                     this.getClass()), this);
1645         }
1646         Properties props = new Properties();
1647         props.putAll(endpointUri.getParams());
1648         // auto set username and password
1649         if (endpointUri.getUserInfo() != null)
1650         {
1651             props.setProperty("username", endpointUri.getUsername());
1652             String passwd = endpointUri.getPassword();
1653             if (passwd != null)
1654             {
1655                 props.setProperty("password", passwd);
1656             }
1657         }
1658         String host = endpointUri.getHost();
1659         if (host != null)
1660         {
1661             props.setProperty("hostname", host);
1662             props.setProperty("host", host);
1663         }
1664         if (endpointUri.getPort() > -1)
1665         {
1666             props.setProperty("port", String.valueOf(endpointUri.getPort()));
1667         }
1668 
1669         org.mule.util.BeanUtils.populateWithoutFail(this, props, true);
1670 
1671         setName(ObjectNameHelper.getConnectorName(this));
1672     }
1673 
1674     /**
1675      * Initialises this connector from its {@link TransportServiceDescriptor} This
1676      * will be called before the {@link #doInitialise()} method is called.
1677      *
1678      * @throws InitialisationException InitialisationException If there are any
1679      *             problems with the configuration or if another exception is thrown
1680      *             it is wrapped in an InitialisationException.
1681      */
1682     protected synchronized void initFromServiceDescriptor() throws InitialisationException
1683     {
1684         try
1685         {
1686             serviceDescriptor = TransportFactory.getServiceDescriptor(getProtocol().toLowerCase(),
1687                 serviceOverrides);
1688 
1689             if (serviceDescriptor.getDispatcherFactory() != null)
1690             {
1691                 logger.debug("Loading DispatcherFactory: " + serviceDescriptor.getDispatcherFactory());
1692                 this.setDispatcherFactory(serviceDescriptor.createDispatcherFactory());
1693             }
1694 
1695             defaultInboundTransformer = serviceDescriptor.createInboundTransformer();
1696             defaultOutboundTransformer = serviceDescriptor.createOutboundTransformer();
1697             defaultResponseTransformer = serviceDescriptor.createResponseTransformer();
1698 
1699             sessionHandler = serviceDescriptor.createSessionHandler();
1700 
1701             // Set any manager default properties for the connector. These are set on
1702             // the Manager with a protocol e.g. jms.specification=1.1
1703             // This provides a really convenient way to set properties on an object
1704             // from unit tests
1705             Map props = new HashMap();
1706             PropertiesUtils.getPropertiesWithPrefix(MuleManager.getInstance().getProperties(), getProtocol()
1707                 .toLowerCase(), props);
1708             if (props.size() > 0)
1709             {
1710                 props = PropertiesUtils.removeNamespaces(props);
1711                 org.mule.util.BeanUtils.populateWithoutFail(this, props, true);
1712             }
1713         }
1714         catch (Exception e)
1715         {
1716             throw new InitialisationException(e, this);
1717         }
1718     }
1719 
1720     /**
1721      * Get the {@link TransportServiceDescriptor} for this connector. This will be
1722      * null if the connector was created by the developer. To create a connector the
1723      * proper way the developer should use the {@link TransportFactory} and pass in
1724      * an endpoint.
1725      *
1726      * @return the {@link TransportServiceDescriptor} for this connector
1727      */
1728     protected TransportServiceDescriptor getServiceDescriptor()
1729     {
1730         if (serviceDescriptor == null)
1731         {
1732             throw new IllegalStateException("This connector has not yet been initialised: " + name);
1733         }
1734         return serviceDescriptor;
1735     }
1736 
1737     /**
1738      * Create a Message receiver for this connector
1739      *
1740      * @param component the component that will receive events from this receiver,
1741      *            the listener
1742      * @param endpoint the endpoint that defies this inbound communication
1743      * @return an instance of the message receiver defined in this connectors'
1744      *         {@link org.mule.providers.service.TransportServiceDescriptor}
1745      *         initialised using the component and endpoint.
1746      * @throws Exception if there is a problem creating the receiver. This exception
1747      *             really depends on the underlying transport, thus any exception
1748      *             could be thrown
1749      */
1750     protected UMOMessageReceiver createReceiver(UMOComponent component, UMOEndpoint endpoint)
1751         throws Exception
1752     {
1753         return getServiceDescriptor().createMessageReceiver(this, component, endpoint);
1754     }
1755 
1756     /**
1757      * Gets a <code>UMOMessageAdapter</code> for the endpoint for the given message
1758      * (data)
1759      *
1760      * @param message the data with which to initialise the
1761      *            <code>UMOMessageAdapter</code>
1762      * @return the <code>UMOMessageAdapter</code> for the endpoint
1763      * @throws org.mule.umo.MessagingException if the message parameter is not
1764      *             supported
1765      * @see org.mule.umo.provider.UMOMessageAdapter
1766      */
1767     public UMOMessageAdapter getMessageAdapter(Object message) throws MessagingException
1768     {
1769         try
1770         {
1771             return serviceDescriptor.createMessageAdapter(message);
1772         }
1773         catch (TransportServiceException e)
1774         {
1775             throw new MessagingException(CoreMessages.failedToCreate("Message Adapter"),
1776                 message, e);
1777         }
1778     }
1779 
1780     /**
1781      * Gets a {@link UMOStreamMessageAdapter} from the connector for the given
1782      * message. This Adapter will correctly handle data streaming for this type of
1783      * connector
1784      *
1785      * @param in the input stream to read the data from
1786      * @param out the outputStream to write data to. This can be null.
1787      * @return the {@link UMOStreamMessageAdapter} for the endpoint
1788      * @throws MessagingException if the message parameter is not supported
1789      * @see UMOStreamMessageAdapter
1790      */
1791     public UMOStreamMessageAdapter getStreamMessageAdapter(InputStream in, OutputStream out)
1792         throws MessagingException
1793     {
1794         try
1795         {
1796             return serviceDescriptor.createStreamMessageAdapter(in, out);
1797         }
1798         catch (TransportServiceException e)
1799         {
1800             throw new MessagingException(CoreMessages.failedToCreate("Stream Message Adapter"),
1801                 in, e);
1802         }
1803     }
1804 
1805     /**
1806      * A map of fully qualified class names that should override those in the
1807      * connectors' service descriptor This map will be null if there are no overrides
1808      *
1809      * @return a map of override values or null
1810      */
1811     public Map getServiceOverrides()
1812     {
1813         return serviceOverrides;
1814     }
1815 
1816     /**
1817      * Set the Service overrides on this connector.
1818      *
1819      * @param serviceOverrides the override values to use
1820      */
1821     public void setServiceOverrides(Map serviceOverrides)
1822     {
1823         this.serviceOverrides = new Properties();
1824         this.serviceOverrides.putAll(serviceOverrides);
1825     }
1826 
1827     /**
1828      * Will get the output stream for this type of transport. Typically this
1829      * will be called only when Streaming is being used on an outbound endpoint.
1830      * If Streaming is not supported by this transport an {@link UnsupportedOperationException}
1831      * is thrown.   Note that the stream MUST release resources on close.  For help doing so, see
1832      * {@link org.mule.impl.model.streaming.CallbackOutputStream}.
1833      *
1834      * @param endpoint the endpoint that releates to this Dispatcher
1835      * @param message the current message being processed
1836      * @return the output stream to use for this request
1837      * @throws UMOException in case of any error
1838      */
1839     public OutputStream getOutputStream(UMOImmutableEndpoint endpoint, UMOMessage message)
1840         throws UMOException
1841     {
1842         throw new UnsupportedOperationException(
1843             CoreMessages.streamingNotSupported(this.getProtocol()).toString());
1844     }
1845 
1846     // @Override
1847     public String toString()
1848     {
1849         final StringBuffer sb = new StringBuffer(120);
1850         sb.append(ClassUtils.getSimpleName(this.getClass()));
1851         sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
1852         sb.append(", started=").append(started);
1853         sb.append(", initialised=").append(initialised);
1854         sb.append(", name='").append(name).append('\'');
1855         sb.append(", disposed=").append(disposed);
1856         sb.append(", numberOfConcurrentTransactedReceivers=").append(numberOfConcurrentTransactedReceivers);
1857         sb.append(", createMultipleTransactedReceivers=").append(createMultipleTransactedReceivers);
1858         sb.append(", connected=").append(connected);
1859         sb.append(", supportedProtocols=").append(supportedProtocols);
1860         sb.append(", serviceOverrides=").append(serviceOverrides);
1861         sb.append('}');
1862         return sb.toString();
1863     }
1864 }