View Javadoc

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