View Javadoc

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