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.construct;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleEvent;
11  import org.mule.api.MuleException;
12  import org.mule.api.config.ThreadingProfile;
13  import org.mule.api.construct.FlowConstruct;
14  import org.mule.api.construct.FlowConstructAware;
15  import org.mule.api.construct.FlowConstructInvalidException;
16  import org.mule.api.context.MuleContextAware;
17  import org.mule.api.exception.MessagingExceptionHandler;
18  import org.mule.api.lifecycle.Disposable;
19  import org.mule.api.lifecycle.Initialisable;
20  import org.mule.api.lifecycle.InitialisationException;
21  import org.mule.api.lifecycle.Lifecycle;
22  import org.mule.api.lifecycle.LifecycleCallback;
23  import org.mule.api.lifecycle.LifecycleException;
24  import org.mule.api.lifecycle.LifecycleState;
25  import org.mule.api.lifecycle.Startable;
26  import org.mule.api.lifecycle.Stoppable;
27  import org.mule.api.processor.MessageProcessor;
28  import org.mule.api.processor.MessageProcessorBuilder;
29  import org.mule.api.processor.MessageProcessorChain;
30  import org.mule.api.processor.MessageProcessorChainBuilder;
31  import org.mule.api.routing.MessageInfoMapping;
32  import org.mule.api.source.MessageSource;
33  import org.mule.config.i18n.CoreMessages;
34  import org.mule.exception.DefaultServiceExceptionStrategy;
35  import org.mule.lifecycle.EmptyLifecycleCallback;
36  import org.mule.management.stats.FlowConstructStatistics;
37  import org.mule.processor.AbstractFilteringMessageProcessor;
38  import org.mule.processor.AbstractInterceptingMessageProcessor;
39  import org.mule.processor.chain.DefaultMessageProcessorChainBuilder;
40  import org.mule.routing.MuleMessageInfoMapping;
41  import org.mule.util.ClassUtils;
42  
43  import java.beans.ExceptionListener;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  
48  /**
49   * Abstract implementation of {@link FlowConstruct} that:
50   * <ul>
51   *  <li>Is constructed with unique name and {@link MuleContext}.
52   *  <li>Uses a {@link MessageSource} as the source of messages.
53   *  <li>Uses a chain of {@link MessageProcessor}s to process messages.
54   *  <li>Has lifecycle and propagates this lifecycle to both {@link MessageSource} and
55   *  {@link MessageProcessor}s in the correct order depending on the lifecycle phase.
56   *  <li>Allows an {@link ExceptionListener} to be set.
57   * </ul>
58   * Implementations of <code>AbstractFlowConstuct</code> should implement
59   * {@link #configureMessageProcessors(org.mule.api.processor.MessageProcessorChainBuilder)} and
60   * {@link #validateConstruct()} to construct the processing chain required and
61   * validate the resulting construct. Validation may include validation of the type of
62   * attributes of the {@link MessageSource}.
63   * <p/>
64   * Implementations may also implement {@link #doInitialise()}, {@link #doStart()},
65   * {@link #doStop()} and {@link #doDispose()} if they need to perform any action on
66   * lifecycle transitions.
67   */
68  public abstract class AbstractFlowConstruct implements FlowConstruct, Lifecycle
69  {
70      protected transient Log logger = LogFactory.getLog(getClass());
71  
72      protected String name;
73      protected MessageSource messageSource;
74      protected MessageProcessorChain messageProcessorChain;
75      protected MessagingExceptionHandler exceptionListener;
76      protected final FlowConstructLifecycleManager lifecycleManager;
77      protected final MuleContext muleContext;
78      protected FlowConstructStatistics statistics;
79      protected MessageInfoMapping messageInfoMapping = new MuleMessageInfoMapping();
80      protected ThreadingProfile threadingProfile;
81      private boolean canProcessMessage = false;
82      
83      /**
84       * The initial states that the flow can be started in
85       */
86      public static final String INITIAL_STATE_STOPPED = "stopped";
87      public static final String INITIAL_STATE_STARTED = "started";
88      
89      /**
90       * Determines the initial state of this flow when the mule starts. Can be
91       * 'stopped' or 'started' (default)
92       */
93      protected String initialState = INITIAL_STATE_STARTED;
94  
95      public AbstractFlowConstruct(String name, MuleContext muleContext)
96      {
97          this.muleContext = muleContext;
98          this.name = name;
99          this.lifecycleManager = new FlowConstructLifecycleManager(this, muleContext);
100         this.exceptionListener = new DefaultServiceExceptionStrategy(muleContext);
101     }
102 
103     public final void initialise() throws InitialisationException
104     {
105         try
106         {
107             lifecycleManager.fireInitialisePhase(new LifecycleCallback<FlowConstruct>()
108             {
109                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
110                 {
111                     createMessageProcessor();
112 
113                     if (messageSource != null)
114                     {
115                         // Wrap chain to decouple lifecycle
116                         messageSource.setListener(new AbstractInterceptingMessageProcessor()
117                         {
118                             public MuleEvent process(MuleEvent event) throws MuleException
119                             {
120                                 return messageProcessorChain.process(event);
121                             }
122                         });
123                     }
124 
125                     injectFlowConstructMuleContext(messageSource);
126                     injectFlowConstructMuleContext(messageProcessorChain);
127                     injectFlowConstructMuleContext(exceptionListener);
128                     initialiseIfInitialisable(messageSource);
129                     initialiseIfInitialisable(messageProcessorChain);
130                     initialiseIfInitialisable(exceptionListener);
131 
132                     doInitialise();
133 
134                     validateConstruct();
135                 }
136             });
137 
138         }
139         catch (InitialisationException e)
140         {
141             throw e;
142         }
143         catch (MuleException e)
144         {
145             throw new InitialisationException(e, this);
146         }
147     }
148 
149     public final void start() throws MuleException
150     {
151         // Check if Initial State is Stopped
152         if(!isStopped() && initialState.equals(INITIAL_STATE_STOPPED))
153         {
154             lifecycleManager.fireStartPhase(new EmptyLifecycleCallback<FlowConstruct>());
155             lifecycleManager.fireStopPhase(new EmptyLifecycleCallback<FlowConstruct>());
156 
157             logger.info("Flow " + name + " has not been started (initial state = 'stopped')");
158             return;
159         }
160         
161         lifecycleManager.fireStartPhase(new LifecycleCallback<FlowConstruct>()
162         {
163             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
164             {
165                 startIfStartable(messageProcessorChain);
166                 startIfStartable(exceptionListener);
167                 canProcessMessage = true;
168                 startIfStartable(messageSource);
169                 doStart();
170             }
171         });
172     }
173 
174     public final void stop() throws MuleException
175     {
176         lifecycleManager.fireStopPhase(new LifecycleCallback<FlowConstruct>()
177         {
178             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
179             {
180                 try
181                 {
182                     stopIfStoppable(messageSource);
183                 }
184                 finally
185                 {
186                     canProcessMessage = false;
187                 }
188                 stopIfStoppable(messageProcessorChain);
189                 stopIfStoppable(exceptionListener);
190                 doStop();
191             }
192         });
193     }
194 
195     public final void dispose()
196     {
197         try
198         {
199             if (isStarted())
200             {
201                 stop();
202             }
203 
204             lifecycleManager.fireDisposePhase(new LifecycleCallback<FlowConstruct>()
205             {
206                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
207                 {
208                     disposeIfDisposable(messageProcessorChain);
209                     disposeIfDisposable(exceptionListener);
210                     disposeIfDisposable(messageSource);
211                     doDispose();
212                 }
213             });
214         }
215         catch (MuleException e)
216         {
217             logger.error("Failed to stop service: " + name, e);
218         }
219     }
220 
221     public ThreadingProfile getThreadingProfile()
222     {
223         return threadingProfile;
224     }
225 
226     public boolean isStarted()
227     {
228         return lifecycleManager.getState().isStarted();
229     }
230 
231     public boolean isStopped()
232     {
233         return lifecycleManager.getState().isStopped();
234     }
235 
236     public boolean isStopping()
237     {
238         return lifecycleManager.getState().isStopping();
239     }
240 
241     /**
242      * Creates a {@link MessageProcessor} that will process messages from the
243      * configured {@link MessageSource}.
244      * <p>
245      * The default implementation of this methods uses a
246      * {@link DefaultMessageProcessorChainBuilder} and allows a chain of
247      * {@link MessageProcessor}s to be configured using the
248      * {@link #configureMessageProcessors(org.mule.api.processor.MessageProcessorChainBuilder)}
249      * method but if you wish to use another {@link MessageProcessorBuilder} or just
250      * a single {@link MessageProcessor} then this method can be overridden and
251      * return a single {@link MessageProcessor} instead.
252      * 
253      * @throws MuleException
254      */
255     protected void createMessageProcessor() throws MuleException
256     {
257         DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(this);
258         builder.setName("'" + getName() + "' processor chain");
259         configureMessageProcessors(builder);
260         messageProcessorChain = builder.build();
261     }
262 
263     /**
264      * Used to configure the processing chain for this <code>FlowConstuct</code by
265      * adding {@link MessageProcessor}s to the chain using the builder provided.
266      * <p>
267      * To use a different builder of to construct a composite
268      * {@link MessageProcessor} manually override {@link #createMessageProcessor()}
269      * instead.
270      * 
271      *
272      *
273      *
274      * @param builder instance of {@link org.mule.processor.chain.DefaultMessageProcessorChainBuilder}
275      * @throws MuleException
276      */
277     protected abstract void configureMessageProcessors(MessageProcessorChainBuilder builder) throws MuleException;
278 
279     public String getName()
280     {
281         return name;
282     }
283 
284     public MessagingExceptionHandler getExceptionListener()
285     {
286         return exceptionListener;
287     }
288 
289     public void setExceptionListener(MessagingExceptionHandler exceptionListener)
290     {
291         this.exceptionListener = exceptionListener;
292     }
293     
294     public String getInitialState()
295     {
296         return initialState;
297     }
298 
299     public void setInitialState(String initialState)
300     {
301         this.initialState = initialState;
302     }    
303 
304     public LifecycleState getLifecycleState()
305     {
306         return lifecycleManager.getState();
307     }
308 
309     public MuleContext getMuleContext()
310     {
311         return muleContext;
312     }
313 
314     public MessageSource getMessageSource()
315     {
316         return messageSource;
317     }
318 
319     public void setMessageSource(MessageSource messageSource)
320     {
321         this.messageSource = messageSource;
322     }
323 
324     public FlowConstructStatistics getStatistics()
325     {
326         return statistics;
327     }
328 
329     public MessageInfoMapping getMessageInfoMapping()
330     {
331         return messageInfoMapping;
332     }
333 
334     public void setMessageInfoMapping(MessageInfoMapping messageInfoMapping)
335     {
336         this.messageInfoMapping = messageInfoMapping;
337     }
338 
339     protected void doInitialise() throws InitialisationException
340     {
341         int threadPoolSize = threadingProfile == null ? 0 : threadingProfile.getMaxThreadsActive();
342         statistics = new FlowConstructStatistics(getConstructType(), name, threadPoolSize);
343         statistics.setEnabled(muleContext.getStatistics().isEnabled());
344         muleContext.getStatistics().add(statistics);
345     }
346 
347     protected void doStart() throws MuleException
348     {
349         // Empty template method
350     }
351 
352     protected void doStop() throws MuleException
353     {
354         // Empty template method
355     }
356 
357     protected void doDispose()
358     {
359         muleContext.getStatistics().remove(statistics);
360     }
361 
362     /**
363      * Validates configured flow construct
364      * 
365      * @throws FlowConstructInvalidException if the flow construct does not pass
366      *             validation
367      */
368     protected void validateConstruct() throws FlowConstructInvalidException
369     {
370         // Empty template method
371     }
372 
373     protected void injectFlowConstructMuleContext(Object candidate)
374     {
375         if (candidate instanceof FlowConstructAware)
376         {
377             ((FlowConstructAware) candidate).setFlowConstruct(this);
378         }
379         if (candidate instanceof MuleContextAware)
380         {
381             ((MuleContextAware) candidate).setMuleContext(muleContext);
382         }
383     }
384 
385     @Override
386     public String toString()
387     {
388         return String.format("%s{%s}", ClassUtils.getSimpleName(this.getClass()), getName());
389     }
390 
391     protected void initialiseIfInitialisable(Object candidate) throws InitialisationException
392     {
393         if (candidate instanceof Initialisable)
394         {
395             ((Initialisable) candidate).initialise();
396         }
397     }
398 
399     protected void startIfStartable(Object candidate) throws MuleException
400     {
401         if (candidate instanceof Startable)
402         {
403             ((Startable) candidate).start();
404         }
405     }
406 
407     protected void stopIfStoppable(Object candidate) throws MuleException
408     {
409         if (candidate instanceof Stoppable)
410         {
411             ((Stoppable) candidate).stop();
412         }
413     }
414 
415     protected void disposeIfDisposable(Object candidate)
416     {
417         if (candidate instanceof Disposable)
418         {
419             ((Disposable) candidate).dispose();
420         }
421     }
422     
423     public MessageProcessorChain getMessageProcessorChain()
424     {
425         return this.messageProcessorChain;
426     }
427 
428     /**
429      * @return the type of construct being created, e.g. "Flow"
430      */
431     public abstract String getConstructType();
432 
433     public class ProcessIfPipelineStartedMessageProcessor extends AbstractFilteringMessageProcessor
434     {
435 
436         @Override
437         protected boolean accept(MuleEvent event)
438         {
439             return canProcessMessage;
440         }
441 
442         @Override
443         protected MuleEvent handleUnaccepted(MuleEvent event) throws LifecycleException
444         {
445             throw new LifecycleException(CoreMessages.isStopped(getName()), event.getMessage());
446         }
447     }
448 }