View Javadoc

1   /*
2    * $Id: AbstractComponent.java 10415 2008-01-21 10:46:37Z 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.impl.model;
12  
13  import org.mule.MuleManager;
14  import org.mule.config.i18n.CoreMessages;
15  import org.mule.config.i18n.MessageFactory;
16  import org.mule.impl.DefaultComponentExceptionStrategy;
17  import org.mule.impl.MuleDescriptor;
18  import org.mule.impl.OptimizedRequestContext;
19  import org.mule.impl.internal.notifications.ComponentNotification;
20  import org.mule.management.stats.ComponentStatistics;
21  import org.mule.providers.AbstractConnector;
22  import org.mule.umo.ComponentException;
23  import org.mule.umo.UMOComponent;
24  import org.mule.umo.UMODescriptor;
25  import org.mule.umo.UMOEvent;
26  import org.mule.umo.UMOException;
27  import org.mule.umo.UMOMessage;
28  import org.mule.umo.endpoint.UMOEndpoint;
29  import org.mule.umo.endpoint.UMOImmutableEndpoint;
30  import org.mule.umo.lifecycle.InitialisationException;
31  import org.mule.umo.model.ModelException;
32  import org.mule.umo.model.UMOModel;
33  import org.mule.umo.provider.DispatchException;
34  import org.mule.umo.provider.UMOMessageReceiver;
35  import org.mule.util.concurrent.WaitableBoolean;
36  
37  import java.beans.ExceptionListener;
38  import java.util.ArrayList;
39  import java.util.Iterator;
40  import java.util.List;
41  
42  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
43  
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  
47  /**
48   * A base implementation for all UMOComponents in Mule
49   */
50  public abstract class AbstractComponent implements UMOComponent
51  {
52      /**
53       * logger used by this class
54       */
55      protected transient Log logger = LogFactory.getLog(getClass());
56  
57      /**
58       * The Mule descriptor associated with the component
59       */
60      protected MuleDescriptor descriptor = null;
61  
62      protected ComponentStatistics stats = null;
63  
64      /**
65       * Determines if the component has been stopped
66       */
67      protected AtomicBoolean stopped = new AtomicBoolean(true);
68  
69      /**
70       * Determines whether stop has been called and is still in progress
71       */
72      protected WaitableBoolean stopping = new WaitableBoolean(false);
73  
74      /**
75       * determines if the proxy pool has been initialised
76       */
77      protected AtomicBoolean poolInitialised = new AtomicBoolean(false);
78  
79      /**
80       * The exception strategy used by the component, this is provided by the
81       * UMODescriptor
82       */
83      protected ExceptionListener exceptionListener = null;
84  
85      /**
86       * Determines if the component has been initilised
87       */
88      protected AtomicBoolean initialised = new AtomicBoolean(false);
89  
90      /**
91       * The model in which this component is registered
92       */
93      protected UMOModel model;
94  
95      /**
96       * Determines if the component has been paused
97       */
98      protected WaitableBoolean paused = new WaitableBoolean(false);
99  
100     /**
101      * Default constructor
102      */
103     public AbstractComponent(MuleDescriptor descriptor, UMOModel model)
104     {
105         if (descriptor == null)
106         {
107             throw new IllegalArgumentException("Descriptor cannot be null");
108         }
109         this.descriptor = descriptor;
110         this.model = model;
111     }
112 
113     /**
114      * Initialise the component. The component will first create a Mule UMO from the
115      * UMODescriptor and then initialise a pool based on the attributes in the
116      * UMODescriptor.
117      * 
118      * @throws org.mule.umo.lifecycle.InitialisationException if the component fails
119      *             to initialise
120      * @see org.mule.umo.UMODescriptor
121      */
122     public final synchronized void initialise() throws InitialisationException
123     {
124         if (initialised.get())
125         {
126             throw new InitialisationException(
127                 CoreMessages.objectAlreadyInitialised("Component '" + descriptor.getName() + "'"), this);
128         }
129         descriptor.initialise();
130 
131         this.exceptionListener = descriptor.getExceptionListener();
132 
133         // initialise statistics
134         stats = createStatistics();
135 
136         stats.setEnabled(((MuleManager) MuleManager.getInstance()).getStatistics().isEnabled());
137         ((MuleManager) MuleManager.getInstance()).getStatistics().add(stats);
138         stats.setOutboundRouterStat(getDescriptor().getOutboundRouter().getStatistics());
139         stats.setInboundRouterStat(getDescriptor().getInboundRouter().getStatistics());
140 
141         doInitialise();
142         initialised.set(true);
143         fireComponentNotification(ComponentNotification.COMPONENT_INITIALISED);
144 
145     }
146 
147     protected ComponentStatistics createStatistics()
148     {
149         return new ComponentStatistics(getName(),
150             descriptor.getThreadingProfile().getMaxThreadsActive());
151     }
152 
153     protected void fireComponentNotification(int action)
154     {
155         MuleManager.getInstance().fireNotification(new ComponentNotification(descriptor, action));
156     }
157 
158     public void forceStop() throws UMOException
159     {
160         if (!stopped.get())
161         {
162             logger.debug("Stopping UMOComponent");
163             stopping.set(true);
164             fireComponentNotification(ComponentNotification.COMPONENT_STOPPING);
165             doForceStop();
166             stopped.set(true);
167             stopping.set(false);
168             fireComponentNotification(ComponentNotification.COMPONENT_STOPPED);
169         }
170     }
171 
172     public void stop() throws UMOException
173     {
174         if (!stopped.get())
175         {
176             logger.debug("Stopping UMOComponent");
177             stopping.set(true);
178             fireComponentNotification(ComponentNotification.COMPONENT_STOPPING);
179 
180             // Unregister Listeners for the component
181             unregisterListeners();
182 
183             doStop();
184             stopped.set(true);
185             initialised.set(false);
186             fireComponentNotification(ComponentNotification.COMPONENT_STOPPED);
187         }
188     }
189 
190     public void start() throws UMOException
191     {
192         start(false);
193     }
194 
195     /**
196      * Starts a Mule Component.
197      * 
198      * @param startPaused - Start component in a "paused" state (messages are
199      *            received but not processed).
200      */
201     protected void start(boolean startPaused) throws UMOException
202     {
203 
204         // Create the receivers for the component but do not start them yet.
205         registerListeners();
206 
207         // We connect the receivers _before_ starting the component because there may
208         // be
209         // some initialization required for the component which needs to have them
210         // connected.
211         // For example, the org.mule.providers.soap.glue.GlueMessageReceiver adds
212         // InitialisationCallbacks within its doConnect() method (see MULE-804).
213         connectListeners();
214 
215         // Start (and pause) the component.
216         if (stopped.get())
217         {
218             stopped.set(false);
219             paused.set(false);
220             doStart();
221         }
222         fireComponentNotification(ComponentNotification.COMPONENT_STARTED);
223         if (startPaused)
224         {
225             pause();
226         }
227 
228         // We start the receivers _after_ starting the component because if a message
229         // gets routed to the component before it is started,
230         // org.mule.impl.model.AbstractComponent.dispatchEvent() will throw a
231         // ComponentException with message COMPONENT_X_IS_STOPPED (see MULE-526).
232         startListeners();
233     }
234 
235     /**
236      * Pauses event processing for a single Mule Component. Unlike stop(), a paused
237      * component will still consume messages from the underlying transport, but those
238      * messages will be queued until the component is resumed.
239      */
240     public final void pause() throws UMOException
241     {
242 
243         doPause();
244         paused.set(true);
245         fireComponentNotification(ComponentNotification.COMPONENT_PAUSED);
246     }
247 
248     /**
249      * Resumes a single Mule Component that has been paused. If the component is not
250      * paused nothing is executed.
251      */
252     public final void resume() throws UMOException
253     {
254         doResume();
255         paused.set(false);
256         fireComponentNotification(ComponentNotification.COMPONENT_RESUMED);
257     }
258 
259     /**
260      * Determines if the component is in a paused state
261      * 
262      * @return True if the component is in a paused state, false otherwise
263      */
264     public boolean isPaused()
265     {
266         return paused.get();
267     }
268 
269     /**
270      * Custom components can execute code necessary to put the component in a paused
271      * state here. If a developer overloads this method the doResume() method MUST
272      * also be overloaded to avoid inconsistent state in the component
273      * 
274      * @throws UMOException
275      */
276     protected void doPause() throws UMOException
277     {
278         // template method
279     }
280 
281     /**
282      * Custom components can execute code necessary to resume a component once it has
283      * been paused If a developer overloads this method the doPause() method MUST
284      * also be overloaded to avoid inconsistent state in the component
285      * 
286      * @throws UMOException
287      */
288     protected void doResume() throws UMOException
289     {
290         // template method
291     }
292 
293     public final void dispose()
294     {
295         try
296         {
297             if (!stopped.get())
298             {
299                 stop();
300             }
301         }
302         catch (UMOException e)
303         {
304             // Failed to stop the component. But since we're being disposed now anyway, we can
305             // only log this failure and continue cleaning up
306             logger.error("Failed to stop component: " + descriptor.getName(), e);
307         }
308         doDispose();
309         fireComponentNotification(ComponentNotification.COMPONENT_DISPOSED);
310         ((MuleManager) MuleManager.getInstance()).getStatistics().remove(stats);
311     }
312 
313     public ComponentStatistics getStatistics()
314     {
315         return stats;
316     }
317 
318     /*
319      * (non-Javadoc)
320      * 
321      * @see org.mule.umo.UMOSession#getDescriptor()
322      */
323     public UMODescriptor getDescriptor()
324     {
325         return descriptor;
326     }
327 
328     public void dispatchEvent(UMOEvent event) throws UMOException
329     {
330         if (stopping.get() || stopped.get())
331         {
332             throw new ComponentException(
333                 CoreMessages.componentIsStopped(this.getDescriptor().getName()), 
334                 event.getMessage(), this);
335         }
336 
337         try
338         {
339             waitIfPaused(event);
340         }
341         catch (InterruptedException e)
342         {
343             throw new ComponentException(event.getMessage(), this, e);
344         }
345 
346         // Dispatching event to an inbound endpoint
347         // in the MuleSession#dispatchEvent
348         UMOImmutableEndpoint endpoint = event.getEndpoint();
349 
350         if (!endpoint.canReceive())
351         {
352             try
353             {
354                 endpoint.dispatch(event);
355             }
356             catch (Exception e)
357             {
358                 throw new DispatchException(event.getMessage(), event.getEndpoint(), e);
359             }
360 
361             return;
362         }
363 
364         // Dispatching event to the component
365         if (stats.isEnabled())
366         {
367             stats.incReceivedEventASync();
368         }
369 
370         if (logger.isDebugEnabled())
371         {
372             logger.debug("Component: " + descriptor.getName() + " has received asynchronous event on: "
373                          + event.getEndpoint().getEndpointURI());
374         }
375 
376         doDispatch(event);
377     }
378 
379     public UMOMessage sendEvent(UMOEvent event) throws UMOException
380     {
381         if (stopping.get() || stopped.get())
382         {
383             throw new ComponentException(
384                 CoreMessages.componentIsStopped(this.getDescriptor().getName()), 
385                 event.getMessage(), this);
386         }
387 
388         try
389         {
390             waitIfPaused(event);
391         }
392         catch (InterruptedException e)
393         {
394             throw new ComponentException(event.getMessage(), this, e);
395         }
396 
397         if (stats.isEnabled())
398         {
399             stats.incReceivedEventSync();
400         }
401         if (logger.isDebugEnabled())
402         {
403             logger.debug("Component: " + descriptor.getName() + " has received synchronous event on: "
404                          + event.getEndpoint().getEndpointURI());
405         }
406         event = OptimizedRequestContext.unsafeSetEvent(event);
407         return doSend(event);
408     }
409 
410     /**
411      * Called before an event is sent or dispatched to a component, it will block
412      * until resume() is called. Users can override this method if they want to
413      * handle pausing differently e.g. implement a store and forward policy
414      * 
415      * @param event the current event being passed to the component
416      * @throws InterruptedException if the thread is interrupted
417      */
418     protected void waitIfPaused(UMOEvent event) throws InterruptedException
419     {
420         if (logger.isDebugEnabled() && paused.get())
421         {
422             logger.debug("Component: " + descriptor.getName()
423                          + " is paused. Blocking call until resume is called");
424         }
425         paused.whenFalse(null);
426     }
427 
428     /**
429      * @return the Mule descriptor name which is associated with the component
430      */
431     public String getName()
432     {
433         return descriptor.getName();
434     }
435 
436     /*
437      * (non-Javadoc)
438      * 
439      * @see java.lang.Object#toString()
440      */
441     public String toString()
442     {
443         return descriptor.getName();
444     }
445 
446     public boolean isStopped()
447     {
448         return stopped.get();
449     }
450 
451     public boolean isStopping()
452     {
453         return stopping.get();
454     }
455 
456     protected void handleException(Exception e)
457     {
458         if (exceptionListener instanceof DefaultComponentExceptionStrategy)
459         {
460             if (((DefaultComponentExceptionStrategy) exceptionListener).getComponent() == null)
461             {
462                 ((DefaultComponentExceptionStrategy) exceptionListener).setComponent(this);
463             }
464         }
465         exceptionListener.exceptionThrown(e);
466     }
467 
468     /**
469      * Provides a consistent mechanism for custom models to create components.
470      * 
471      * @return
472      * @throws UMOException
473      */
474     protected Object lookupComponent() throws UMOException
475     {
476         return ComponentFactory.createComponent(getDescriptor());
477     }
478 
479     protected void doForceStop() throws UMOException
480     {
481         // template method
482     }
483 
484     protected void doStop() throws UMOException
485     {
486         // template method
487     }
488 
489     protected void doStart() throws UMOException
490     {
491         // template method
492     }
493 
494     protected void doDispose()
495     {
496         // template method
497     }
498 
499     protected void doInitialise() throws InitialisationException
500     {
501         // template method
502     }
503 
504     public boolean isStarted()
505     {
506         return !stopped.get();
507     }
508 
509     protected abstract UMOMessage doSend(UMOEvent event) throws UMOException;
510 
511     protected abstract void doDispatch(UMOEvent event) throws UMOException;
512 
513     public Object getInstance() throws UMOException
514     {
515         return lookupComponent();
516     }
517 
518     protected void registerListeners() throws UMOException
519     {
520         UMOEndpoint endpoint;
521         List endpoints = getIncomingEndpoints();
522 
523         for (Iterator it = endpoints.iterator(); it.hasNext();)
524         {
525             endpoint = (UMOEndpoint) it.next();
526             try
527             {
528                 endpoint.getConnector().registerListener(this, endpoint);
529             }
530             catch (UMOException e)
531             {
532                 throw e;
533             }
534             catch (Exception e)
535             {
536                 throw new ModelException(
537                     CoreMessages.failedtoRegisterOnEndpoint(this.getDescriptor().getName(),
538                         endpoint.getEndpointURI()), e);
539             }
540         }
541     }
542 
543     protected void unregisterListeners() throws UMOException
544     {
545         UMOEndpoint endpoint;
546         List endpoints = getIncomingEndpoints();
547 
548         for (Iterator it = endpoints.iterator(); it.hasNext();)
549         {
550             endpoint = (UMOEndpoint) it.next();
551             try
552             {
553                 endpoint.getConnector().unregisterListener(this, endpoint);
554             }
555             catch (UMOException e)
556             {
557                 throw e;
558             }
559             catch (Exception e)
560             {
561                 throw new ModelException(
562                     CoreMessages.failedToUnregister(this.getDescriptor().getName(), 
563                         endpoint.getEndpointURI()), e);
564             }
565         }
566     }
567 
568     protected void startListeners() throws UMOException
569     {
570         UMOEndpoint endpoint;
571         List endpoints = getIncomingEndpoints();
572 
573         for (Iterator it = endpoints.iterator(); it.hasNext();)
574         {
575             endpoint = (UMOEndpoint) it.next();
576             UMOMessageReceiver receiver = ((AbstractConnector) endpoint.getConnector()).getReceiver(this,
577                 endpoint);
578             if (receiver != null && endpoint.getConnector().isStarted()
579                 && endpoint.getInitialState().equals(UMOEndpoint.INITIAL_STATE_STARTED))
580             {
581                 receiver.start();
582             }
583         }
584     }
585 
586     protected void stopListeners() throws UMOException
587     {
588         UMOEndpoint endpoint;
589         List endpoints = getIncomingEndpoints();
590 
591         for (Iterator it = endpoints.iterator(); it.hasNext();)
592         {
593             endpoint = (UMOEndpoint) it.next();
594             UMOMessageReceiver receiver = ((AbstractConnector) endpoint.getConnector()).getReceiver(this,
595                 endpoint);
596             if (receiver != null)
597             {
598                 receiver.stop();
599             }
600         }
601     }
602 
603     protected void connectListeners() throws UMOException
604     {
605         UMOEndpoint endpoint;
606         List endpoints = getIncomingEndpoints();
607 
608         for (Iterator it = endpoints.iterator(); it.hasNext();)
609         {
610             endpoint = (UMOEndpoint) it.next();
611             UMOMessageReceiver receiver = ((AbstractConnector) endpoint.getConnector()).getReceiver(this,
612                 endpoint);
613             if (receiver != null)
614             {
615                 try
616                 {
617                     receiver.connect();
618                 }
619                 catch (Exception e)
620                 {
621                     throw new ModelException(
622                         MessageFactory.createStaticMessage("Failed to connect listener "
623                                     + receiver + " for endpoint " + endpoint.getName()),
624                         e);
625                 }
626             }
627         }
628     }
629 
630     protected void disconnectListeners() throws UMOException
631     {
632         UMOEndpoint endpoint;
633         List endpoints = getIncomingEndpoints();
634 
635         for (Iterator it = endpoints.iterator(); it.hasNext();)
636         {
637             endpoint = (UMOEndpoint) it.next();
638             UMOMessageReceiver receiver = ((AbstractConnector) endpoint.getConnector()).getReceiver(this,
639                 endpoint);
640             if (receiver != null)
641             {
642                 try
643                 {
644                     receiver.disconnect();
645                 }
646                 catch (Exception e)
647                 {
648                     throw new ModelException(
649                         MessageFactory.createStaticMessage("Failed to disconnect listener "
650                                     + receiver + " for endpoint " + endpoint.getName()),
651                         e);
652                 }
653             }
654         }
655     }
656 
657     /**
658      * Returns a list of all incoming endpoints on a component.
659      */
660     protected List getIncomingEndpoints()
661     {
662         List endpoints = new ArrayList();
663 
664         // Add inbound endpoints
665         endpoints.addAll(getDescriptor().getInboundRouter().getEndpoints());
666         // Add the (deprecated) single inbound endpoint.
667         if (getDescriptor().getInboundEndpoint() != null)
668         {
669             endpoints.add(getDescriptor().getInboundEndpoint());
670         }
671 
672         // Add response endpoints
673         if (getDescriptor().getResponseRouter() != null
674             && getDescriptor().getResponseRouter().getEndpoints() != null)
675         {
676             endpoints.addAll(getDescriptor().getResponseRouter().getEndpoints());
677         }
678         return endpoints;
679     }
680 
681 }