View Javadoc

1   /*
2    * $Id: AbstractService.java 22742 2011-08-25 20:58:42Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.service;
12  
13  import org.mule.DefaultMuleEvent;
14  import org.mule.RequestContext;
15  import org.mule.api.AnnotatedObject;
16  import org.mule.api.MuleContext;
17  import org.mule.api.MuleEvent;
18  import org.mule.api.MuleException;
19  import org.mule.api.MuleRuntimeException;
20  import org.mule.api.MuleSession;
21  import org.mule.api.component.Component;
22  import org.mule.api.construct.FlowConstruct;
23  import org.mule.api.construct.FlowConstructAware;
24  import org.mule.api.context.MuleContextAware;
25  import org.mule.api.exception.MessagingExceptionHandler;
26  import org.mule.api.lifecycle.Disposable;
27  import org.mule.api.lifecycle.Initialisable;
28  import org.mule.api.lifecycle.InitialisationException;
29  import org.mule.api.lifecycle.LifecycleCallback;
30  import org.mule.api.lifecycle.LifecycleManager;
31  import org.mule.api.lifecycle.LifecycleState;
32  import org.mule.api.lifecycle.Startable;
33  import org.mule.api.lifecycle.Stoppable;
34  import org.mule.api.model.Model;
35  import org.mule.api.processor.MessageProcessor;
36  import org.mule.api.processor.MessageProcessorChain;
37  import org.mule.api.processor.MessageProcessorChainBuilder;
38  import org.mule.api.routing.MessageInfoMapping;
39  import org.mule.api.routing.OutboundRouterCollection;
40  import org.mule.api.routing.RouterStatisticsRecorder;
41  import org.mule.api.service.Service;
42  import org.mule.api.source.MessageSource;
43  import org.mule.component.simple.PassThroughComponent;
44  import org.mule.config.i18n.CoreMessages;
45  import org.mule.lifecycle.EmptyLifecycleCallback;
46  import org.mule.lifecycle.processor.ProcessIfStartedWaitIfPausedMessageProcessor;
47  import org.mule.management.stats.RouterStatistics;
48  import org.mule.management.stats.ServiceStatistics;
49  import org.mule.processor.AbstractInterceptingMessageProcessor;
50  import org.mule.processor.chain.DefaultMessageProcessorChainBuilder;
51  import org.mule.routing.MuleMessageInfoMapping;
52  import org.mule.routing.outbound.DefaultOutboundRouterCollection;
53  import org.mule.service.processor.ServiceAsyncRequestReplyRequestor;
54  import org.mule.session.DefaultMuleSession;
55  import org.mule.util.ClassUtils;
56  
57  import java.util.Collections;
58  import java.util.Map;
59  import java.util.concurrent.ConcurrentHashMap;
60  
61  import javax.xml.namespace.QName;
62  
63  import org.apache.commons.logging.Log;
64  import org.apache.commons.logging.LogFactory;
65  
66  /**
67   * A base implementation for all Services in Mule
68   */
69  public abstract class AbstractService implements Service, MessageProcessor, AnnotatedObject
70  {
71  
72      /**
73       * logger used by this class
74       */
75      protected transient Log logger = LogFactory.getLog(getClass());
76  
77      protected ServiceStatistics stats;
78      /**
79       * The model in which this service is registered
80       */
81      protected Model model;
82  
83      protected MuleContext muleContext;
84  
85      protected ServiceLifecycleManager lifecycleManager;
86  
87      /**
88       * The initial states that the service can be started in
89       */
90      public static final String INITIAL_STATE_STOPPED = "stopped";
91      public static final String INITIAL_STATE_STARTED = "started";
92      public static final String INITIAL_STATE_PAUSED = "paused";
93  
94      /**
95       * The exception strategy used by the service.
96       */
97      protected MessagingExceptionHandler exceptionListener;
98  
99      /**
100      * The service's name
101      */
102     protected String name;
103 
104     protected MessageProcessor outboundRouter = new DefaultOutboundRouterCollection();
105 
106     protected MessageSource messageSource = new ServiceCompositeMessageSource();
107     protected ServiceAsyncReplyCompositeMessageSource asyncReplyMessageSource = new ServiceAsyncReplyCompositeMessageSource();
108 
109     protected MessageProcessorChain messageProcessorChain;
110     protected MessageInfoMapping messageInfoMapping = new MuleMessageInfoMapping();
111     private final Map<QName, Object> annotations = new ConcurrentHashMap<QName, Object>();
112 
113     /**
114      * Determines the initial state of this service when the model starts. Can be
115      * 'stopped' or 'started' (default)
116      */
117     protected String initialState = INITIAL_STATE_STARTED;
118 
119     // Default component to use if one is not configured.
120     // TODO MULE-3113 This should not really be needed as to implement bridging we
121     // should
122     // should just increment a 'bridged' counter and sent the event straight to
123     // outbound router collection. Currently it's the Component that routes events
124     // onto the outbound router collection so this default implementation is needed.
125     // It would be beneficial to differenciate between component invocations and
126     // events that are bridged but currently everything is an invocation.
127     protected Component component = new PassThroughComponent();
128 
129     public AbstractService(MuleContext muleContext)
130     {
131         this.muleContext = muleContext;
132         ((MuleContextAware) component).setMuleContext(muleContext);
133         try
134         {
135             lifecycleManager = new ServiceLifecycleManager(this, muleContext);
136         }
137         catch (MuleException e)
138         {
139             throw new MuleRuntimeException(CoreMessages.failedToCreate("Service Lifecycle Manager"), e);
140         }
141 
142     }
143 
144     //----------------------------------------------------------------------------------------//
145     //-                    LIFECYCLE METHODS
146     //----------------------------------------------------------------------------------------//
147 
148     /**
149      * Initialise the service. The service will first create a component from the
150      * ServiceDescriptor and then initialise a pool based on the attributes in the
151      * ServiceDescriptor .
152      *
153      * @throws org.mule.api.lifecycle.InitialisationException
154      *          if the service fails to
155      *          initialise
156      * @see org.mule.api.registry.ServiceDescriptor
157      */
158     public final synchronized void initialise() throws InitialisationException
159     {
160         try
161         {
162             lifecycleManager.fireInitialisePhase(new LifecycleCallback<FlowConstruct>()
163             {
164                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
165                 {
166                     if (outboundRouter instanceof MuleContextAware)
167                     {
168                         ((MuleContextAware) outboundRouter).setMuleContext(muleContext);
169                     }
170 
171                     if (exceptionListener == null)
172                     {
173                         // By default use the model Exception Listener
174                         // TODO MULE-2102 This should be configured in the default template.
175                         exceptionListener = getModel().getExceptionListener();
176                     }
177 
178                     injectFlowConstructMuleContext(messageSource);
179                     injectFlowConstructMuleContext(asyncReplyMessageSource);
180                     injectFlowConstructMuleContext(messageProcessorChain);
181                     injectFlowConstructMuleContext(component);
182                     injectFlowConstructMuleContext(exceptionListener);
183                     
184                     doInitialise();
185                 }
186             });
187         }
188         catch (InitialisationException e)
189         {
190             throw e;
191         }
192         catch (MuleException e)
193         {
194             throw new InitialisationException(e, this);
195         }
196 
197     }
198 
199     public void start() throws MuleException
200     {
201         if (!isStopped() && initialState.equals(AbstractService.INITIAL_STATE_STOPPED))
202         {
203             //Transition to a stopped state without changing state of the flow construct
204             lifecycleManager.fireStartPhase(new EmptyLifecycleCallback<FlowConstruct>());
205             lifecycleManager.fireStopPhase(new EmptyLifecycleCallback<FlowConstruct>());
206 
207             logger.info("Service " + name + " has not been started (initial state = 'stopped')");
208             return;
209         }
210         
211         lifecycleManager.fireStartPhase(new LifecycleCallback<FlowConstruct>()
212         {
213             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
214             {
215                 doStart();
216             }
217         });
218 
219         //Cannot call one lifecycle phase from within another, so we pause if necessary here
220         if ( initialState.equals(AbstractService.INITIAL_STATE_PAUSED))
221         {
222             pause();
223             logger.info("Service " + name + " has been started and paused (initial state = 'paused')");
224         }
225 
226     }
227 
228     /**
229      * Pauses event processing for a single Mule Service. Unlike stop(), a paused
230      * service will still consume messages from the underlying transport, but those
231      * messages will be queued until the service is resumed.
232      */
233     public final void pause() throws MuleException
234     {
235         lifecycleManager.firePausePhase(new LifecycleCallback<FlowConstruct>()
236         {
237             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
238             {
239                 doPause();
240             }
241         });
242     }
243 
244     /**
245      * Resumes a single Mule Service that has been paused. If the service is not
246      * paused nothing is executed.
247      */
248     public final void resume() throws MuleException
249     {
250         lifecycleManager.fireResumePhase(new LifecycleCallback<FlowConstruct>()
251         {
252             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
253             {
254                 doResume();
255             }
256         });
257     }
258 
259 
260     public void stop() throws MuleException
261     {
262         lifecycleManager.fireStopPhase(new LifecycleCallback<FlowConstruct>()
263         {
264             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
265             {
266                 doStop();
267             }
268         });
269     }
270 
271     public final void dispose()
272     {
273 
274         try
275         {
276             if (isStarted() || isPaused())
277             {
278                 stop();
279             }
280 
281             lifecycleManager.fireDisposePhase(new LifecycleCallback<FlowConstruct>()
282             {
283                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
284                 {
285                     doDispose();
286                 }
287             });
288         }
289         catch (MuleException e)
290         {
291             logger.error("Failed to stop service: " + name, e);
292         }
293     }
294 
295     public LifecycleState getLifecycleState()
296     {
297         return lifecycleManager.getState();
298     }
299 
300     public boolean isStarted()
301     {
302         return lifecycleManager.getState().isStarted();
303     }
304 
305     /**
306      * Determines if the service is in a paused state
307      * 
308      * @return True if the service is in a paused state, false otherwise
309      */
310     public boolean isPaused()
311     {
312         return lifecycleManager.getCurrentPhase().equals(Pausable.PHASE_NAME);
313     }
314 
315     public boolean isStopped()
316     {
317         return lifecycleManager.getState().isStopped();
318     }
319 
320     public boolean isStopping()
321     {
322         return lifecycleManager.getState().isStopping();
323     }
324 
325     /**
326      * Custom components can execute code necessary to put the service in a paused state here. If a developer
327      * overloads this method the doResume() method MUST also be overloaded to avoid inconsistent state in the
328      * service
329      * 
330      * @throws MuleException
331      */
332     protected void doPause() throws MuleException
333     {
334         // template method
335     }
336 
337     /**
338      * Custom components can execute code necessary to resume a service once it has
339      * been paused If a developer overloads this method the doPause() method MUST
340      * also be overloaded to avoid inconsistent state in the service
341      *
342      * @throws MuleException
343      */
344     protected void doResume() throws MuleException
345     {
346         // template method
347     }
348 
349     protected void doForceStop() throws MuleException
350     {
351         // template method
352     }
353 
354     protected void doStop() throws MuleException
355     {
356         stopIfStoppable(messageSource);
357         asyncReplyMessageSource.stop();
358 
359         stopIfStoppable(messageProcessorChain);
360         // Component is not in chain
361         stopIfStoppable(component);
362         stopIfStoppable(exceptionListener);
363     }
364 
365     protected void doStart() throws MuleException
366     {
367         // Component is not in chain
368         startIfStartable(exceptionListener);
369         startIfStartable(component);
370         startIfStartable(messageProcessorChain);
371 
372         startIfStartable(messageSource);
373         if (asyncReplyMessageSource.getEndpoints().size() > 0)
374         {
375             asyncReplyMessageSource.start();
376         }
377     }
378 
379     protected void doDispose()
380     {
381         // Component is not in chain
382         disposeIfDisposable(component);
383         disposeIfDisposable(messageProcessorChain);
384         disposeIfDisposable(messageSource);
385         disposeIfDisposable(exceptionListener);
386         muleContext.getStatistics().remove(stats);
387     }
388 
389     protected void doInitialise() throws InitialisationException
390     {
391         // initialise statistics
392         stats = createStatistics();
393         stats.setEnabled(muleContext.getStatistics().isEnabled());
394         muleContext.getStatistics().add(stats);
395         RouterStatistics routerStatistics = null;
396 
397         // If the router collection already has router statistics, keep using them.
398         if (outboundRouter instanceof OutboundRouterCollection)
399         {
400             routerStatistics = ((OutboundRouterCollection)outboundRouter).getRouterStatistics();
401         }
402         if (routerStatistics == null)
403         {
404             routerStatistics = new RouterStatistics(RouterStatistics.TYPE_OUTBOUND);
405         }
406         stats.setOutboundRouterStat(routerStatistics);
407         if (outboundRouter != null && outboundRouter instanceof RouterStatisticsRecorder)
408         {
409             ((RouterStatisticsRecorder)outboundRouter).setRouterStatistics(routerStatistics);
410         }
411         RouterStatistics inboundRouterStatistics = new RouterStatistics(RouterStatistics.TYPE_INBOUND);
412         stats.setInboundRouterStat(inboundRouterStatistics);
413         if (messageSource instanceof RouterStatisticsRecorder)
414         {
415             ((RouterStatisticsRecorder) messageSource).setRouterStatistics(inboundRouterStatistics);
416         }
417         stats.setComponentStat(component.getStatistics());
418 
419         try
420         {
421             buildServiceMessageProcessorChain();
422             injectFlowConstructMuleContext(messageProcessorChain);
423         }
424         catch (MuleException e)
425         {
426             throw new InitialisationException(e, this);
427         }
428         
429         // Wrap chain to decouple lifecycle
430         messageSource.setListener(new AbstractInterceptingMessageProcessor()
431         {
432             public MuleEvent process(MuleEvent event) throws MuleException
433             {
434                 return messageProcessorChain.process(event);
435             }
436         });
437 
438         initialiseIfInitialisable(exceptionListener);
439         initialiseIfInitialisable(component);
440         initialiseIfInitialisable(messageProcessorChain);
441         initialiseIfInitialisable(messageSource);
442         
443         if (asyncReplyMessageSource.getEndpoints().size() > 0)
444         {
445             asyncReplyMessageSource.initialise();
446         }
447     }
448 
449     public void forceStop() throws MuleException
450     {
451         // Kepping this here since I don't understand why this method exists. AFAICS
452         // this just says the service is stopped
453         // without actually stopping it
454         // if (!stopped.get())
455         // {
456         // logger.debug("Stopping Service");
457         // stopping.set(true);
458         // fireServiceNotification(ServiceNotification.SERVICE_STOPPING);
459         // doForceStop();
460         // stopped.set(true);
461         // stopping.set(false);
462         // fireServiceNotification(ServiceNotification.SERVICE_STOPPED);
463         // }
464         doForceStop();
465         stop();
466     }
467 
468 
469     //----------------------------------------------------------------------------------------//
470     //-                    END LIFECYCLE METHODS
471     //----------------------------------------------------------------------------------------//
472 
473     protected void buildServiceMessageProcessorChain() throws MuleException
474     {
475         DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(this);
476         builder.setName("Service '" + name + "' Processor Chain");
477         builder.chain(getServiceStartedAssertingMessageProcessor());
478         addMessageProcessors(builder);
479         messageProcessorChain = builder.build();
480     }
481 
482     protected MessageProcessor getServiceStartedAssertingMessageProcessor()
483     {
484         return new ProcessIfStartedWaitIfPausedMessageProcessor(this, lifecycleManager.getState());
485     }
486 
487     protected abstract void addMessageProcessors(MessageProcessorChainBuilder builder);
488 
489     protected ServiceStatistics createStatistics()
490     {
491         return new ServiceStatistics(name);
492     }
493 
494     public ServiceStatistics getStatistics()
495     {
496         return stats;
497     }
498 
499     @Deprecated
500     public void dispatchEvent(MuleEvent event) throws MuleException
501     {
502         messageProcessorChain.process(event);
503     }
504 
505     @Deprecated
506     public MuleEvent sendEvent(MuleEvent event) throws MuleException
507     {
508         return messageProcessorChain.process(event);
509     }
510 
511     /**
512      * @return the Mule descriptor name which is associated with the service
513      */
514     public String getName()
515     {
516         return name;
517     }
518 
519     @Override
520     public String toString()
521     {
522         return String.format("%s{%s}", ClassUtils.getSimpleName(this.getClass()), getName());
523     }
524 
525 
526     // /////////////////////////////////////////////////////////////////////////////////////////
527     // Getters and Setters
528     // /////////////////////////////////////////////////////////////////////////////////////////
529 
530     public Model getModel()
531     {
532         return model;
533     }
534 
535     public void setModel(Model model)
536     {
537         this.model = model;
538     }
539 
540     public MessagingExceptionHandler getExceptionListener()
541     {
542         return exceptionListener;
543     }
544 
545     public void setExceptionListener(MessagingExceptionHandler exceptionListener)
546     {
547         this.exceptionListener = exceptionListener;
548     }
549 
550     public MessageSource getMessageSource()
551     {
552         return messageSource;
553     }
554 
555     public void setMessageSource(MessageSource inboundMessageSource)
556     {
557         this.messageSource = inboundMessageSource;
558     }
559 
560     public MessageProcessor getOutboundMessageProcessor()
561     {
562         return outboundRouter;
563     }
564 
565     // TODO Use spring factory bean
566     @Deprecated
567     public void setMessageProcessor(MessageProcessor processor)
568     {
569         setOutboundMessageProcessor(processor);
570     }
571     
572     public void setOutboundMessageProcessor(MessageProcessor processor)
573     {
574         this.outboundRouter = processor;
575     }
576 
577     public ServiceAsyncReplyCompositeMessageSource getAsyncReplyMessageSource()
578     {
579         return asyncReplyMessageSource;
580     }
581 
582     public void setAsyncReplyMessageSource(ServiceAsyncReplyCompositeMessageSource asyncReplyMessageSource)
583     {
584         this.asyncReplyMessageSource = asyncReplyMessageSource;
585     }
586 
587     public String getInitialState()
588     {
589         return initialState;
590     }
591 
592     public void setInitialState(String initialState)
593     {
594         this.initialState = initialState;
595     }
596 
597     public void setName(String name)
598     {
599         this.name = name;
600     }
601 
602     public Component getComponent()
603     {
604         return component;
605     }
606 
607     public void setComponent(Component component)
608     {
609         this.component = component;
610         this.component.setFlowConstruct(this);
611         if (component instanceof MuleContextAware)
612         {
613             ((MuleContextAware) component).setMuleContext(muleContext);
614 
615         }
616     }
617 
618     public MuleContext getMuleContext()
619     {
620         return muleContext;
621     }
622 
623     public LifecycleManager getLifecycleManager()
624     {
625         return lifecycleManager;
626     }
627     
628     public MessageInfoMapping getMessageInfoMapping()
629     {
630         return messageInfoMapping;
631     }
632 
633     public void setMessageInfoMapping(MessageInfoMapping messageInfoMapping)
634     {
635         this.messageInfoMapping = messageInfoMapping;
636     }
637 
638     protected long getAsyncReplyTimeout()
639     {
640         if (asyncReplyMessageSource.getTimeout() != null)
641         {
642             return asyncReplyMessageSource.getTimeout().longValue();
643         }
644         else
645         {
646             return muleContext.getConfiguration().getDefaultResponseTimeout();
647         }
648     }
649 
650     protected ServiceAsyncRequestReplyRequestor createAsyncReplyProcessor()
651     {
652         ServiceAsyncRequestReplyRequestor asyncReplyMessageProcessor = new ServiceAsyncRequestReplyRequestor();
653         asyncReplyMessageProcessor.setTimeout(getAsyncReplyTimeout());
654         asyncReplyMessageProcessor.setFailOnTimeout(asyncReplyMessageSource.isFailOnTimeout());
655         asyncReplyMessageProcessor.setReplySource(asyncReplyMessageSource);
656         return asyncReplyMessageProcessor;
657     }
658     
659     public MuleEvent process(MuleEvent event) throws MuleException
660     {
661         MuleSession calledSession = new DefaultMuleSession(event.getSession(), this);
662         MuleEvent newEvent = new DefaultMuleEvent(event.getMessage(), event, calledSession);
663         RequestContext.setEvent(newEvent);
664         try
665         {
666             MuleEvent result = messageProcessorChain.process(newEvent);
667             if (result != null)
668             {
669                 result.getMessage().release();
670             }
671             return result;
672         }
673         catch (Exception e)
674         {
675             return getExceptionListener().handleException(e, newEvent);
676         }
677         finally
678         {
679             RequestContext.setEvent(event);
680             event.getMessage().release();
681         }
682     }
683 
684     public MessageProcessorChain getMessageProcessorChain()
685     {
686         return messageProcessorChain;
687     }
688     
689     protected void injectFlowConstructMuleContext(Object candidate)
690     {
691         if (candidate instanceof FlowConstructAware)
692         {
693             ((FlowConstructAware) candidate).setFlowConstruct(this);
694         }
695         if (candidate instanceof MuleContextAware)
696         {
697             ((MuleContextAware) candidate).setMuleContext(muleContext);
698         }
699     }
700 
701     protected void initialiseIfInitialisable(Object candidate) throws InitialisationException
702     {
703         if (candidate instanceof Initialisable)
704         {
705             ((Initialisable) candidate).initialise();
706         }
707     }
708 
709     protected void startIfStartable(Object candidate) throws MuleException
710     {
711         if (candidate instanceof Startable)
712         {
713             ((Startable) candidate).start();
714         }
715     }
716 
717     protected void stopIfStoppable(Object candidate) throws MuleException
718     {
719         if (candidate instanceof Stoppable)
720         {
721             ((Stoppable) candidate).stop();
722         }
723     }
724 
725     protected void disposeIfDisposable(Object candidate)
726     {
727         if (candidate instanceof Disposable)
728         {
729             ((Disposable) candidate).dispose();
730         }
731     }
732 
733     protected void pauseIfPausable(Object candidate) throws MuleException
734     {
735         if (candidate instanceof Pausable)
736         {
737             ((Pausable) candidate).pause();
738         }
739     }
740 
741     protected void resumeIfResumable(Object candidate) throws MuleException
742     {
743         if (candidate instanceof Resumable)
744         {
745             ((Resumable) candidate).resume();
746         }
747     }
748 
749     public final Object getAnnotation(QName name)
750     {
751         return annotations.get(name);
752     }
753 
754     public final Map<QName, Object> getAnnotations()
755     {
756         return Collections.unmodifiableMap(annotations);
757     }
758 
759     public synchronized final void setAnnotations(Map<QName, Object> newAnnotations)
760     {
761         annotations.clear();
762         annotations.putAll(newAnnotations);
763     }
764 }