View Javadoc

1   /*
2    * $Id: AbstractService.java 19210 2010-08-26 12:09:16Z 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.component.Component;
20  import org.mule.api.construct.FlowConstruct;
21  import org.mule.api.construct.FlowConstructAware;
22  import org.mule.api.context.MuleContextAware;
23  import org.mule.api.exception.MessagingExceptionHandler;
24  import org.mule.api.lifecycle.Disposable;
25  import org.mule.api.lifecycle.Initialisable;
26  import org.mule.api.lifecycle.InitialisationException;
27  import org.mule.api.lifecycle.LifecycleCallback;
28  import org.mule.api.lifecycle.LifecycleManager;
29  import org.mule.api.lifecycle.LifecycleState;
30  import org.mule.api.lifecycle.Startable;
31  import org.mule.api.lifecycle.Stoppable;
32  import org.mule.api.model.Model;
33  import org.mule.api.processor.MessageProcessor;
34  import org.mule.api.routing.MessageInfoMapping;
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.builder.InterceptingChainMessageProcessorBuilder;
46  import org.mule.routing.MuleMessageInfoMapping;
47  import org.mule.routing.outbound.DefaultOutboundRouterCollection;
48  import org.mule.service.processor.ServiceAsyncRequestReplyRequestor;
49  import org.mule.util.ClassUtils;
50  
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  
54  /**
55   * A base implementation for all Services in Mule
56   */
57  public abstract class AbstractService implements Service, MessageProcessor
58  {
59  
60      /**
61       * logger used by this class
62       */
63      protected transient Log logger = LogFactory.getLog(getClass());
64  
65      protected ServiceStatistics stats;
66      /**
67       * The model in which this service is registered
68       */
69      protected Model model;
70  
71      protected MuleContext muleContext;
72  
73      protected ServiceLifecycleManager lifecycleManager;
74  
75      /**
76       * The initial states that the service can be started in
77       */
78      public static final String INITIAL_STATE_STOPPED = "stopped";
79      public static final String INITIAL_STATE_STARTED = "started";
80      public static final String INITIAL_STATE_PAUSED = "paused";
81  
82      /**
83       * The exception strategy used by the service.
84       */
85      protected MessagingExceptionHandler exceptionListener;
86  
87      /**
88       * The service's name
89       */
90      protected String name;
91  
92      protected MessageProcessor outboundRouter = new DefaultOutboundRouterCollection();
93  
94      protected MessageSource messageSource = new ServiceCompositeMessageSource();
95      protected ServiceAsyncReplyCompositeMessageSource asyncReplyMessageSource = new ServiceAsyncReplyCompositeMessageSource();
96  
97      protected MessageProcessor messageProcessorChain;
98      protected MessageInfoMapping messageInfoMapping = new MuleMessageInfoMapping();
99  
100     /**
101      * Determines the initial state of this service when the model starts. Can be
102      * 'stopped' or 'started' (default)
103      */
104     protected String initialState = INITIAL_STATE_STARTED;
105 
106     // Default component to use if one is not configured.
107     // TODO MULE-3113 This should not really be needed as to implement bridging we
108     // should
109     // should just increment a 'bridged' counter and sent the event straight to
110     // outbound router collection. Currently it's the Component that routes events
111     // onto the outbound router collection so this default implementation is needed.
112     // It would be beneficial to differenciate between component invocations and
113     // events that are bridged but currently everything is an invocation.
114     protected Component component = new PassThroughComponent();
115 
116     public AbstractService(MuleContext muleContext)
117     {
118         this.muleContext = muleContext;
119         try
120         {
121             lifecycleManager = new ServiceLifecycleManager(this);
122         }
123         catch (MuleException e)
124         {
125             throw new MuleRuntimeException(CoreMessages.failedToCreate("Service Lifecycle Manager"), e);
126         }
127 
128     }
129 
130     //----------------------------------------------------------------------------------------//
131     //-                    LIFECYCLE METHODS
132     //----------------------------------------------------------------------------------------//
133 
134     /**
135      * Initialise the service. The service will first create a component from the
136      * ServiceDescriptor and then initialise a pool based on the attributes in the
137      * ServiceDescriptor .
138      *
139      * @throws org.mule.api.lifecycle.InitialisationException
140      *          if the service fails to
141      *          initialise
142      * @see org.mule.api.registry.ServiceDescriptor
143      */
144     public final synchronized void initialise() throws InitialisationException
145     {
146         try
147         {
148             lifecycleManager.fireInitialisePhase(new LifecycleCallback<FlowConstruct>()
149             {
150                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
151                 {
152                     if (outboundRouter instanceof MuleContextAware)
153                     {
154                         ((MuleContextAware) outboundRouter).setMuleContext(muleContext);
155                     }
156 
157                     if (exceptionListener == null)
158                     {
159                         // By default use the model Exception Listener
160                         // TODO MULE-2102 This should be configured in the default template.
161                         exceptionListener = getModel().getExceptionListener();
162                     }
163 
164                     if (messageSource instanceof FlowConstructAware)
165                     {
166                         ((FlowConstructAware) messageSource).setFlowConstruct(object);
167                     }
168                     asyncReplyMessageSource.setFlowConstruct(object);
169                     // Ensure Component has service instance and is initialised. If the component
170                     // was configured with spring and is therefore in the registry it will get
171                     // started automatically, if it was set on the service directly then it won't
172                     // be started automatically. So to be sure we start it here.
173                     component.setFlowConstruct(object);
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         if (messageSource instanceof Stoppable)
348         {
349             ((Stoppable) messageSource).stop();
350         }
351         asyncReplyMessageSource.stop();
352 
353         // Component is not in chain
354         if (component instanceof Stoppable)
355         {
356             ((Stoppable) component).stop();
357         }
358         if (messageProcessorChain instanceof Stoppable)
359         {
360             ((Stoppable) messageProcessorChain).stop();
361         }
362     }
363 
364     protected void doStart() throws MuleException
365     {
366         // Component is not in chain
367         if (component instanceof Startable)
368         {
369             ((Startable) component).start();
370         }
371         if (messageProcessorChain instanceof Startable)
372         {
373             ((Startable) messageProcessorChain).start();
374         }
375 
376         if (messageSource instanceof Startable)
377         {
378             ((Startable) messageSource).start();
379         }
380         if (asyncReplyMessageSource.getEndpoints().size() > 0)
381         {
382             asyncReplyMessageSource.start();
383         }
384     }
385 
386     protected void doDispose()
387     {
388         // Component is not in chain
389         if (component instanceof Disposable)
390         {
391             ((Disposable) component).dispose();
392         }
393         if (messageProcessorChain instanceof Disposable)
394         {
395             ((Disposable) messageProcessorChain).dispose();
396         }
397         if (messageSource instanceof Disposable)
398         {
399             ((Disposable) messageSource).dispose();
400         }
401         muleContext.getStatistics().remove(stats);
402     }
403 
404     protected void doInitialise() throws InitialisationException
405     {
406         // initialise statistics
407         stats = createStatistics();
408         stats.setEnabled(muleContext.getStatistics().isEnabled());
409         muleContext.getStatistics().add(stats);
410         RouterStatistics routerStatistics = new RouterStatistics(RouterStatistics.TYPE_OUTBOUND);
411         stats.setOutboundRouterStat(routerStatistics);
412         if (outboundRouter != null && outboundRouter instanceof RouterStatisticsRecorder)
413         {
414             ((RouterStatisticsRecorder)outboundRouter).setRouterStatistics(routerStatistics);
415         }
416         RouterStatistics inboundRouterStatistics = new RouterStatistics(RouterStatistics.TYPE_INBOUND);
417         stats.setInboundRouterStat(inboundRouterStatistics);
418         if (messageSource instanceof RouterStatisticsRecorder)
419         {
420             ((RouterStatisticsRecorder) messageSource).setRouterStatistics(inboundRouterStatistics);
421         }
422         stats.setComponentStat(component.getStatistics());
423 
424         try
425         {
426             buildServiceMessageProcessorChain();
427         }
428         catch (MuleException e)
429         {
430             throw new InitialisationException(e, this);
431         }
432         
433         // Wrap chain to decouple lifecycle
434         messageSource.setListener(new AbstractInterceptingMessageProcessor()
435         {
436             public MuleEvent process(MuleEvent event) throws MuleException
437             {
438                 return messageProcessorChain.process(event);
439             }
440         });
441 
442         // Component is not in chain
443         if (component instanceof Initialisable)
444         {
445             ((Initialisable) component).initialise();
446         }
447         if (messageProcessorChain instanceof Initialisable)
448         {
449             ((Initialisable) messageProcessorChain).initialise();
450         }
451         if (messageSource instanceof Initialisable)
452         {
453             ((Initialisable) messageSource).initialise();
454         }
455         if (asyncReplyMessageSource.getEndpoints().size() > 0)
456         {
457             asyncReplyMessageSource.initialise();
458         }
459 
460     }
461 
462     public void forceStop() throws MuleException
463     {
464         // Kepping this here since I don't understand why this method exists. AFAICS
465         // this just says the service is stopped
466         // without actually stopping it
467         // if (!stopped.get())
468         // {
469         // logger.debug("Stopping Service");
470         // stopping.set(true);
471         // fireServiceNotification(ServiceNotification.SERVICE_STOPPING);
472         // doForceStop();
473         // stopped.set(true);
474         // stopping.set(false);
475         // fireServiceNotification(ServiceNotification.SERVICE_STOPPED);
476         // }
477         doForceStop();
478         stop();
479     }
480 
481 
482     //----------------------------------------------------------------------------------------//
483     //-                    END LIFECYCLE METHODS
484     //----------------------------------------------------------------------------------------//
485 
486     protected void buildServiceMessageProcessorChain() throws MuleException
487     {
488         InterceptingChainMessageProcessorBuilder builder = new InterceptingChainMessageProcessorBuilder(this);
489         builder.setName("Service '" + name + "' Processor Chain");
490         builder.chain(getServiceStartedAssertingMessageProcessor());
491         addMessageProcessors(builder);
492         messageProcessorChain = builder.build();
493     }
494 
495     protected MessageProcessor getServiceStartedAssertingMessageProcessor()
496     {
497         return new ProcessIfStartedWaitIfPausedMessageProcessor(this, lifecycleManager.getState());
498     }
499 
500     protected abstract void addMessageProcessors(InterceptingChainMessageProcessorBuilder builder);
501 
502     protected ServiceStatistics createStatistics()
503     {
504         return new ServiceStatistics(name);
505     }
506 
507     public ServiceStatistics getStatistics()
508     {
509         return stats;
510     }
511 
512     @Deprecated
513     public void dispatchEvent(MuleEvent event) throws MuleException
514     {
515         messageProcessorChain.process(event);
516     }
517 
518     @Deprecated
519     public MuleEvent sendEvent(MuleEvent event) throws MuleException
520     {
521         return messageProcessorChain.process(event);
522     }
523 
524     /**
525      * @return the Mule descriptor name which is associated with the service
526      */
527     public String getName()
528     {
529         return name;
530     }
531 
532     @Override
533     public String toString()
534     {
535         return String.format("%s{%s}", ClassUtils.getSimpleName(this.getClass()), getName());
536     }
537 
538 
539     // /////////////////////////////////////////////////////////////////////////////////////////
540     // Getters and Setters
541     // /////////////////////////////////////////////////////////////////////////////////////////
542 
543     public Model getModel()
544     {
545         return model;
546     }
547 
548     public void setModel(Model model)
549     {
550         this.model = model;
551     }
552 
553     public MessagingExceptionHandler getExceptionListener()
554     {
555         return exceptionListener;
556     }
557 
558     public void setExceptionListener(MessagingExceptionHandler exceptionListener)
559     {
560         this.exceptionListener = exceptionListener;
561     }
562 
563     public MessageSource getMessageSource()
564     {
565         return messageSource;
566     }
567 
568     public void setMessageSource(MessageSource inboundMessageSource)
569     {
570         this.messageSource = inboundMessageSource;
571     }
572 
573     public MessageProcessor getOutboundMessageProcessor()
574     {
575         return outboundRouter;
576     }
577 
578     // TODO Use spring factory bean
579     @Deprecated
580     public void setMessageProcessor(MessageProcessor processor)
581     {
582         setOutboundMessageProcessor(processor);
583     }
584     
585     public void setOutboundMessageProcessor(MessageProcessor processor)
586     {
587         this.outboundRouter = processor;
588     }
589 
590     public ServiceAsyncReplyCompositeMessageSource getAsyncReplyMessageSource()
591     {
592         return asyncReplyMessageSource;
593     }
594 
595     public void setAsyncReplyMessageSource(ServiceAsyncReplyCompositeMessageSource asyncReplyMessageSource)
596     {
597         this.asyncReplyMessageSource = asyncReplyMessageSource;
598     }
599 
600     public String getInitialState()
601     {
602         return initialState;
603     }
604 
605     public void setInitialState(String initialState)
606     {
607         this.initialState = initialState;
608     }
609 
610     public void setName(String name)
611     {
612         this.name = name;
613     }
614 
615     public Component getComponent()
616     {
617         return component;
618     }
619 
620     public void setComponent(Component component)
621     {
622         this.component = component;
623         this.component.setFlowConstruct(this);
624     }
625 
626     public MuleContext getMuleContext()
627     {
628         return muleContext;
629     }
630 
631     public LifecycleManager getLifecycleManager()
632     {
633         return lifecycleManager;
634     }
635     
636     public MessageInfoMapping getMessageInfoMapping()
637     {
638         return messageInfoMapping;
639     }
640 
641     public void setMessageInfoMapping(MessageInfoMapping messageInfoMapping)
642     {
643         this.messageInfoMapping = messageInfoMapping;
644     }
645 
646     protected long getAsyncReplyTimeout()
647     {
648         if (asyncReplyMessageSource.getTimeout() != null)
649         {
650             return asyncReplyMessageSource.getTimeout().longValue();
651         }
652         else
653         {
654             return muleContext.getConfiguration().getDefaultResponseTimeout();
655         }
656     }
657 
658     protected ServiceAsyncRequestReplyRequestor createAsyncReplyProcessor()
659     {
660         ServiceAsyncRequestReplyRequestor asyncReplyMessageProcessor = new ServiceAsyncRequestReplyRequestor();
661         asyncReplyMessageProcessor.setTimeout(getAsyncReplyTimeout());
662         asyncReplyMessageProcessor.setFailOnTimeout(asyncReplyMessageSource.isFailOnTimeout());
663         asyncReplyMessageProcessor.setReplySource(asyncReplyMessageSource);
664         return asyncReplyMessageProcessor;
665     }
666     
667     public MuleEvent process(MuleEvent event) throws MuleException
668     {
669         MuleEvent newEvent = new DefaultMuleEvent(event.getMessage(), event.getEndpoint(), this, event);
670         RequestContext.setEvent(newEvent);
671         return messageProcessorChain.process(newEvent);
672     }
673 
674 }