View Javadoc

1   /*
2    * $Id$
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.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.construct.FlowConstruct;
19  import org.mule.api.construct.FlowConstructAware;
20  import org.mule.api.construct.FlowConstructInvalidException;
21  import org.mule.api.context.MuleContextAware;
22  import org.mule.api.exception.MessagingExceptionHandler;
23  import org.mule.api.lifecycle.Disposable;
24  import org.mule.api.lifecycle.Initialisable;
25  import org.mule.api.lifecycle.InitialisationException;
26  import org.mule.api.lifecycle.Lifecycle;
27  import org.mule.api.lifecycle.LifecycleCallback;
28  import org.mule.api.lifecycle.LifecycleState;
29  import org.mule.api.lifecycle.Startable;
30  import org.mule.api.lifecycle.Stoppable;
31  import org.mule.api.processor.MessageProcessor;
32  import org.mule.api.processor.MessageProcessorBuilder;
33  import org.mule.api.routing.MessageInfoMapping;
34  import org.mule.api.source.MessageSource;
35  import org.mule.exception.DefaultServiceExceptionStrategy;
36  import org.mule.management.stats.FlowConstructStatistics;
37  import org.mule.processor.AbstractInterceptingMessageProcessor;
38  import org.mule.processor.builder.InterceptingChainMessageProcessorBuilder;
39  import org.mule.routing.MuleMessageInfoMapping;
40  import org.mule.util.ClassUtils;
41  
42  import java.beans.ExceptionListener;
43  
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  
47  /**
48   * Abstract implementation of {@link FlowConstruct} that: <li>Is constructed with
49   * unique name and {@link MuleContext}. <li>Uses a {@link MessageSource} as the
50   * source of messages. <li>Uses a chain of {@link MessageProcessor}s to process
51   * messages. <li>Has lifecycle and propagates this lifecycle to both
52   * {@link MessageSource} and {@link MessageProcessor}s in the correct order depending
53   * on the lifecycle phase. <li>Allows an {@link ExceptionListener} to be set. <br/>
54   * Implementations of <code>AbstractFlowConstuct</code> should implement
55   * {@link#configureMessageProcessors(ChainMessageProcessorBuilder)} and
56   * {@link #validateConstruct()} to construct the processing chain required and
57   * validate the resulting construct. Validation may include validation of the type of
58   * attributes of the {@link MessageSource}.
59   * <p/>
60   * Implementations may also implement {@link #doInitialise()}, {@link #doStart()},
61   * {@link #doStop()} and {@link #doDispose()} if they need to perform any action on
62   * lifecycle transitions.
63   */
64  public abstract class AbstractFlowConstruct implements FlowConstruct, Lifecycle, MessageProcessor
65  {
66      protected transient Log logger = LogFactory.getLog(getClass());
67  
68      protected String name;
69      protected MessageSource messageSource;
70      protected MessageProcessor messageProcessorChain;
71      protected MessagingExceptionHandler exceptionListener;
72      protected final FlowConstructLifecycleManager lifecycleManager;
73      protected final MuleContext muleContext;
74      protected final FlowConstructStatistics statistics;
75      protected MessageInfoMapping messageInfoMapping = new MuleMessageInfoMapping();
76  
77      public AbstractFlowConstruct(String name, MuleContext muleContext)
78      {
79          this.muleContext = muleContext;
80          this.name = name;
81          this.lifecycleManager = new FlowConstructLifecycleManager(this);
82          this.statistics = new FlowConstructStatistics(name);
83          this.exceptionListener = new DefaultServiceExceptionStrategy(muleContext);
84      }
85  
86      public final void initialise() throws InitialisationException
87      {
88          try
89          {
90              lifecycleManager.fireInitialisePhase(new LifecycleCallback<FlowConstruct>()
91              {
92                  public void onTransition(String phaseName, FlowConstruct object) throws MuleException
93                  {
94                      createMessageProcessor();
95  
96                      if (messageSource != null)
97                      {
98                          // Wrap chain to decouple lifecycle
99                          messageSource.setListener(new AbstractInterceptingMessageProcessor()
100                         {
101                             public MuleEvent process(MuleEvent event) throws MuleException
102                             {
103                                 return messageProcessorChain.process(event);
104                             }
105                         });
106                     }
107 
108                     injectFlowConstructMuleContext(messageSource);
109                     injectFlowConstructMuleContext(messageProcessorChain);
110                     initialiseIfInitialisable(messageSource);
111                     initialiseIfInitialisable(messageProcessorChain);
112 
113                     doInitialise();
114 
115                     validateConstruct();
116                 }
117             });
118 
119         }
120         catch (InitialisationException e)
121         {
122             throw e;
123         }
124         catch (MuleException e)
125         {
126             throw new InitialisationException(e, this);
127         }
128     }
129 
130     public final void start() throws MuleException
131     {
132         lifecycleManager.fireStartPhase(new LifecycleCallback<FlowConstruct>()
133         {
134             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
135             {
136                 startIfStartable(messageProcessorChain);
137                 startIfStartable(messageSource);
138                 doStart();
139             }
140         });
141     }
142 
143     public final void stop() throws MuleException
144     {
145         lifecycleManager.fireStopPhase(new LifecycleCallback<FlowConstruct>()
146         {
147             public void onTransition(String phaseName, FlowConstruct object) throws MuleException
148             {
149                 stopIfStoppable(messageSource);
150                 stopIfStoppable(messageProcessorChain);
151                 doStop();
152             }
153         });
154     }
155 
156     public final void dispose()
157     {
158         try
159         {
160             if (isStarted())
161             {
162                 stop();
163             }
164 
165             lifecycleManager.fireDisposePhase(new LifecycleCallback<FlowConstruct>()
166             {
167                 public void onTransition(String phaseName, FlowConstruct object) throws MuleException
168                 {
169                     disposeIfDisposable(messageProcessorChain);
170                     disposeIfDisposable(messageSource);
171                     doDispose();
172                 }
173             });
174         }
175         catch (MuleException e)
176         {
177             logger.error("Failed to stop service: " + name, e);
178         }
179     }
180 
181     public boolean isStarted()
182     {
183         return lifecycleManager.getState().isStarted();
184     }
185 
186     public boolean isStopped()
187     {
188         return lifecycleManager.getState().isStopped();
189     }
190 
191     public boolean isStopping()
192     {
193         return lifecycleManager.getState().isStopping();
194     }
195 
196     /**
197      * Creates a {@link MessageProcessor} that will process messages from the
198      * configured {@link MessageSource}.
199      * <p>
200      * The default implementation of this methods uses a
201      * {@link InterceptingChainMessageProcessorBuilder} and allows a chain of
202      * {@link MessageProcessor}s to be configured using the
203      * {@link #configureMessageProcessors(InterceptingChainMessageProcessorBuilder)}
204      * method but if you wish to use another {@link MessageProcessorBuilder} or just
205      * a single {@link MessageProcessor} then this method can be overridden and
206      * return a single {@link MessageProcessor} instead.
207      * 
208      * @throws MuleException
209      */
210     protected void createMessageProcessor() throws MuleException
211     {
212         InterceptingChainMessageProcessorBuilder builder = new InterceptingChainMessageProcessorBuilder(this);
213         configureMessageProcessors(builder);
214         messageProcessorChain = builder.build();
215     }
216 
217     /**
218      * Used to configure the processing chain for this <code>FlowConstuct</code by
219      * adding {@link MessageProcessor}s to the chain using the builder provided.
220      * <p>
221      * To use a different builder of to construct a composite
222      * {@link MessageProcessor} manually override {@link #createMessageProcessor()}
223      * instead.
224      * 
225      * @param builder instance of {@link InterceptingChainMessageProcessorBuilder}
226      * @throws MuleException 
227      */
228     protected abstract void configureMessageProcessors(InterceptingChainMessageProcessorBuilder builder) throws MuleException;
229 
230     public String getName()
231     {
232         return name;
233     }
234 
235     public MessagingExceptionHandler getExceptionListener()
236     {
237         return exceptionListener;
238     }
239 
240     public void setExceptionListener(MessagingExceptionHandler exceptionListener)
241     {
242         this.exceptionListener = exceptionListener;
243     }
244 
245     public LifecycleState getLifecycleState()
246     {
247         return lifecycleManager.getState();
248     }
249 
250     public MuleContext getMuleContext()
251     {
252         return muleContext;
253     }
254 
255     public MessageSource getMessageSource()
256     {
257         return messageSource;
258     }
259 
260     public void setMessageSource(MessageSource messageSource)
261     {
262         this.messageSource = messageSource;
263     }
264 
265     public FlowConstructStatistics getStatistics()
266     {
267         return statistics;
268     }
269 
270     public MessageInfoMapping getMessageInfoMapping()
271     {
272         return messageInfoMapping;
273     }
274 
275     public void setMessageInfoMapping(MessageInfoMapping messageInfoMapping)
276     {
277         this.messageInfoMapping = messageInfoMapping;
278     }
279 
280     protected void doInitialise() throws InitialisationException
281     {
282         // Empty template method
283     }
284 
285     protected void doStart() throws MuleException
286     {
287         // Empty template method
288     }
289 
290     protected void doStop() throws MuleException
291     {
292         // Empty template method
293     }
294 
295     protected void doDispose()
296     {
297         // Empty template method
298     }
299 
300     /**
301      * Validates configured flow construct
302      * 
303      * @throws FlowConstructInvalidException if the flow construct does not pass
304      *             validation
305      */
306     protected void validateConstruct() throws FlowConstructInvalidException
307     {
308         // Empty template method
309     }
310 
311     private void injectFlowConstructMuleContext(Object candidate)
312     {
313         if (candidate instanceof FlowConstructAware)
314         {
315             ((FlowConstructAware) candidate).setFlowConstruct(this);
316         }
317         if (candidate instanceof MuleContextAware)
318         {
319             ((MuleContextAware) candidate).setMuleContext(muleContext);
320         }
321     }
322 
323     @Override
324     public String toString()
325     {
326         return String.format("%s{%s}", ClassUtils.getSimpleName(this.getClass()), getName());
327     }
328 
329     private void initialiseIfInitialisable(Object candidate) throws InitialisationException
330     {
331         if (candidate instanceof Initialisable)
332         {
333             ((Initialisable) candidate).initialise();
334         }
335     }
336 
337     private void startIfStartable(Object candidate) throws MuleException
338     {
339         if (candidate instanceof Startable)
340         {
341             ((Startable) candidate).start();
342         }
343     }
344 
345     private void stopIfStoppable(Object candidate) throws MuleException
346     {
347         if (candidate instanceof Stoppable)
348         {
349             ((Stoppable) candidate).stop();
350         }
351     }
352 
353     private void disposeIfDisposable(Object candidate)
354     {
355         if (candidate instanceof Disposable)
356         {
357             ((Disposable) candidate).dispose();
358         }
359     }
360     
361     public MuleEvent process(MuleEvent event) throws MuleException
362     {
363         MuleEvent newEvent = new DefaultMuleEvent(event.getMessage(), event.getEndpoint(), this, event);
364         RequestContext.setEvent(newEvent);
365         return messageProcessorChain.process(newEvent);
366     }
367 
368 }