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