View Javadoc

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