View Javadoc

1   /*
2    * $Id: AbstractFlowConstruct.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.construct;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleException;
16  import org.mule.api.config.ThreadingProfile;
17  import org.mule.api.construct.FlowConstruct;
18  import org.mule.api.construct.FlowConstructAware;
19  import org.mule.api.construct.FlowConstructInvalidException;
20  import org.mule.api.context.MuleContextAware;
21  import org.mule.api.exception.MessagingExceptionHandler;
22  import org.mule.api.lifecycle.Disposable;
23  import org.mule.api.lifecycle.Initialisable;
24  import org.mule.api.lifecycle.InitialisationException;
25  import org.mule.api.lifecycle.Lifecycle;
26  import org.mule.api.lifecycle.LifecycleCallback;
27  import org.mule.api.lifecycle.LifecycleState;
28  import org.mule.api.lifecycle.Startable;
29  import org.mule.api.lifecycle.Stoppable;
30  import org.mule.api.processor.MessageProcessor;
31  import org.mule.api.processor.MessageProcessorBuilder;
32  import org.mule.api.processor.MessageProcessorChain;
33  import org.mule.api.processor.MessageProcessorChainBuilder;
34  import org.mule.api.routing.MessageInfoMapping;
35  import org.mule.api.source.MessageSource;
36  import org.mule.exception.DefaultServiceExceptionStrategy;
37  import org.mule.management.stats.FlowConstructStatistics;
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  
82      public AbstractFlowConstruct(String name, MuleContext muleContext)
83      {
84          this.muleContext = muleContext;
85          this.name = name;
86          this.lifecycleManager = new FlowConstructLifecycleManager(this, muleContext);
87          this.exceptionListener = new DefaultServiceExceptionStrategy(muleContext);
88      }
89  
90      public final void initialise() throws InitialisationException
91      {
92          try
93          {
94              lifecycleManager.fireInitialisePhase(new LifecycleCallback<FlowConstruct>()
95              {
96                  public void onTransition(String phaseName, FlowConstruct object) throws MuleException
97                  {
98                      createMessageProcessor();
99  
100                     if (messageSource != null)
101                     {
102                         // Wrap chain to decouple lifecycle
103                         messageSource.setListener(new AbstractInterceptingMessageProcessor()
104                         {
105                             public MuleEvent process(MuleEvent event) throws MuleException
106                             {
107                                 return messageProcessorChain.process(event);
108                             }
109                         });
110                     }
111 
112                     injectFlowConstructMuleContext(messageSource);
113                     injectFlowConstructMuleContext(messageProcessorChain);
114                     injectFlowConstructMuleContext(exceptionListener);
115                     initialiseIfInitialisable(messageSource);
116                     initialiseIfInitialisable(messageProcessorChain);
117                     initialiseIfInitialisable(exceptionListener);
118 
119                     doInitialise();
120 
121                     validateConstruct();
122                 }
123             });
124 
125         }
126         catch (InitialisationException e)
127         {
128             throw e;
129         }
130         catch (MuleException e)
131         {
132             throw new InitialisationException(e, this);
133         }
134     }
135 
136     public final void start() throws MuleException
137     {
138         lifecycleManager.fireStartPhase(new LifecycleCallback<FlowConstruct>()
139         {
140             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
141             {
142                 startIfStartable(messageProcessorChain);
143                 startIfStartable(exceptionListener);
144                 startIfStartable(messageSource);
145                 doStart();
146             }
147         });
148     }
149 
150     public final void stop() throws MuleException
151     {
152         lifecycleManager.fireStopPhase(new LifecycleCallback<FlowConstruct>()
153         {
154             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
155             {
156                 stopIfStoppable(messageSource);
157                 stopIfStoppable(messageProcessorChain);
158                 stopIfStoppable(exceptionListener);
159                 doStop();
160             }
161         });
162     }
163 
164     public final void dispose()
165     {
166         try
167         {
168             if (isStarted())
169             {
170                 stop();
171             }
172 
173             lifecycleManager.fireDisposePhase(new LifecycleCallback<FlowConstruct>()
174             {
175                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
176                 {
177                     disposeIfDisposable(messageProcessorChain);
178                     disposeIfDisposable(exceptionListener);
179                     disposeIfDisposable(messageSource);
180                     doDispose();
181                 }
182             });
183         }
184         catch (MuleException e)
185         {
186             logger.error("Failed to stop service: " + name, e);
187         }
188     }
189 
190     public ThreadingProfile getThreadingProfile()
191     {
192         return threadingProfile;
193     }
194 
195     public boolean isStarted()
196     {
197         return lifecycleManager.getState().isStarted();
198     }
199 
200     public boolean isStopped()
201     {
202         return lifecycleManager.getState().isStopped();
203     }
204 
205     public boolean isStopping()
206     {
207         return lifecycleManager.getState().isStopping();
208     }
209 
210     /**
211      * Creates a {@link MessageProcessor} that will process messages from the
212      * configured {@link MessageSource}.
213      * <p>
214      * The default implementation of this methods uses a
215      * {@link DefaultMessageProcessorChainBuilder} and allows a chain of
216      * {@link MessageProcessor}s to be configured using the
217      * {@link #configureMessageProcessors(org.mule.api.processor.MessageProcessorChainBuilder)}
218      * method but if you wish to use another {@link MessageProcessorBuilder} or just
219      * a single {@link MessageProcessor} then this method can be overridden and
220      * return a single {@link MessageProcessor} instead.
221      * 
222      * @throws MuleException
223      */
224     protected void createMessageProcessor() throws MuleException
225     {
226         DefaultMessageProcessorChainBuilder builder = new DefaultMessageProcessorChainBuilder(this);
227         configureMessageProcessors(builder);
228         messageProcessorChain = builder.build();
229     }
230 
231     /**
232      * Used to configure the processing chain for this <code>FlowConstuct</code by
233      * adding {@link MessageProcessor}s to the chain using the builder provided.
234      * <p>
235      * To use a different builder of to construct a composite
236      * {@link MessageProcessor} manually override {@link #createMessageProcessor()}
237      * instead.
238      * 
239      *
240      *
241      *
242      * @param builder instance of {@link org.mule.processor.chain.DefaultMessageProcessorChainBuilder}
243      * @throws MuleException
244      */
245     protected abstract void configureMessageProcessors(MessageProcessorChainBuilder builder) throws MuleException;
246 
247     public String getName()
248     {
249         return name;
250     }
251 
252     public MessagingExceptionHandler getExceptionListener()
253     {
254         return exceptionListener;
255     }
256 
257     public void setExceptionListener(MessagingExceptionHandler exceptionListener)
258     {
259         this.exceptionListener = exceptionListener;
260     }
261 
262     public LifecycleState getLifecycleState()
263     {
264         return lifecycleManager.getState();
265     }
266 
267     public MuleContext getMuleContext()
268     {
269         return muleContext;
270     }
271 
272     public MessageSource getMessageSource()
273     {
274         return messageSource;
275     }
276 
277     public void setMessageSource(MessageSource messageSource)
278     {
279         this.messageSource = messageSource;
280     }
281 
282     public FlowConstructStatistics getStatistics()
283     {
284         return statistics;
285     }
286 
287     public MessageInfoMapping getMessageInfoMapping()
288     {
289         return messageInfoMapping;
290     }
291 
292     public void setMessageInfoMapping(MessageInfoMapping messageInfoMapping)
293     {
294         this.messageInfoMapping = messageInfoMapping;
295     }
296 
297     protected void doInitialise() throws InitialisationException
298     {
299         int threadPoolSize = threadingProfile == null ? 0 : threadingProfile.getMaxThreadsActive();
300         statistics = new FlowConstructStatistics(getConstructType(), name, threadPoolSize);
301         statistics.setEnabled(muleContext.getStatistics().isEnabled());
302         muleContext.getStatistics().add(statistics);
303     }
304 
305     protected void doStart() throws MuleException
306     {
307         // Empty template method
308     }
309 
310     protected void doStop() throws MuleException
311     {
312         // Empty template method
313     }
314 
315     protected void doDispose()
316     {
317         muleContext.getStatistics().remove(statistics);
318     }
319 
320     /**
321      * Validates configured flow construct
322      * 
323      * @throws FlowConstructInvalidException if the flow construct does not pass
324      *             validation
325      */
326     protected void validateConstruct() throws FlowConstructInvalidException
327     {
328         // Empty template method
329     }
330 
331     protected void injectFlowConstructMuleContext(Object candidate)
332     {
333         if (candidate instanceof FlowConstructAware)
334         {
335             ((FlowConstructAware) candidate).setFlowConstruct(this);
336         }
337         if (candidate instanceof MuleContextAware)
338         {
339             ((MuleContextAware) candidate).setMuleContext(muleContext);
340         }
341     }
342 
343     @Override
344     public String toString()
345     {
346         return String.format("%s{%s}", ClassUtils.getSimpleName(this.getClass()), getName());
347     }
348 
349     protected void initialiseIfInitialisable(Object candidate) throws InitialisationException
350     {
351         if (candidate instanceof Initialisable)
352         {
353             ((Initialisable) candidate).initialise();
354         }
355     }
356 
357     protected void startIfStartable(Object candidate) throws MuleException
358     {
359         if (candidate instanceof Startable)
360         {
361             ((Startable) candidate).start();
362         }
363     }
364 
365     protected void stopIfStoppable(Object candidate) throws MuleException
366     {
367         if (candidate instanceof Stoppable)
368         {
369             ((Stoppable) candidate).stop();
370         }
371     }
372 
373     protected void disposeIfDisposable(Object candidate)
374     {
375         if (candidate instanceof Disposable)
376         {
377             ((Disposable) candidate).dispose();
378         }
379     }
380     
381     public MessageProcessorChain getMessageProcessorChain()
382     {
383         return this.messageProcessorChain;
384     }
385 
386     /**
387      *
388      * @return the type of construct being created, e.g. "Flow"
389      */
390     public abstract String getConstructType();
391 }