View Javadoc

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