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.component;
8   
9   import org.mule.DefaultMuleEventContext;
10  import org.mule.RequestContext;
11  import org.mule.api.DefaultMuleException;
12  import org.mule.api.MuleContext;
13  import org.mule.api.MuleEvent;
14  import org.mule.api.MuleEventContext;
15  import org.mule.api.MuleException;
16  import org.mule.api.component.JavaComponent;
17  import org.mule.api.component.LifecycleAdapter;
18  import org.mule.api.construct.FlowConstruct;
19  import org.mule.api.lifecycle.Disposable;
20  import org.mule.api.lifecycle.Initialisable;
21  import org.mule.api.lifecycle.InitialisationException;
22  import org.mule.api.lifecycle.Startable;
23  import org.mule.api.lifecycle.Stoppable;
24  import org.mule.api.model.EntryPointResolverSet;
25  import org.mule.config.i18n.CoreMessages;
26  import org.mule.config.i18n.MessageFactory;
27  import org.mule.model.resolvers.LegacyEntryPointResolverSet;
28  import org.mule.registry.JSR250ValidatorProcessor;
29  import org.mule.util.annotation.AnnotationMetaData;
30  import org.mule.util.annotation.AnnotationUtils;
31  
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.util.List;
35  
36  import javax.annotation.PostConstruct;
37  import javax.annotation.PreDestroy;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  
42  /**
43   * <code>DefaultComponentLifecycleAdapter</code> is a default implementation of
44   * {@link LifecycleAdapter} for use with {@link JavaComponent} that expects component
45   * instances to implement Mule lifecycle interfaces in order to receive lifecycle. Lifecycle interfaces supported are -
46   * <ul>
47   * <li>{@link org.mule.api.lifecycle.Initialisable}</li>
48   * <li>{@link org.mule.api.lifecycle.Startable}</li>
49   * <li>{@link org.mule.api.lifecycle.Stoppable}</li>
50   * <li>{@link org.mule.api.lifecycle.Disposable}</li>
51   * </ul>
52   *  This implementation also supports JSR-250 lifecycle annotations
53   *  {@link javax.annotation.PostConstruct} (for initialisation) and/or {@link javax.annotation.PreDestroy}
54   * (for disposal of the object). Only one of each annotation can be used per component object.
55   *
56   * @see org.mule.registry.JSR250ValidatorProcessor for details about the rules for using JSR-250 lifecycle annotations
57   */
58  public class DefaultComponentLifecycleAdapter implements LifecycleAdapter
59  {
60      /**
61       * logger used by this class
62       */
63      protected static final Log logger = LogFactory.getLog(DefaultComponentLifecycleAdapter.class);
64  
65      protected Object componentObject;
66  
67      protected JavaComponent component;
68      protected EntryPointResolverSet entryPointResolver;
69      protected FlowConstruct flowConstruct;
70  
71      protected boolean isInitialisable = false;
72      protected boolean isStartable = false;
73      protected boolean isStoppable = false;
74      protected boolean isDisposable = false;
75  
76      protected Method initMethod;
77      protected Method disposeMethod;
78  
79      private boolean started = false;
80      private boolean disposed = false;
81  
82      protected MuleContext muleContext;
83  
84      public DefaultComponentLifecycleAdapter(Object componentObject,
85                                              JavaComponent component,
86                                              FlowConstruct flowConstruct,
87                                              MuleContext muleContext) throws MuleException
88      {
89          if (muleContext == null)
90          {
91              throw new IllegalStateException("No muleContext provided");
92          }
93          if (componentObject == null)
94          {
95              throw new IllegalArgumentException("POJO Service cannot be null");
96          }
97  
98          if (entryPointResolver == null)
99          {
100             entryPointResolver = new LegacyEntryPointResolverSet();
101         }
102         this.componentObject = componentObject;
103         this.component = component;
104         this.flowConstruct = flowConstruct;
105 
106         // save a ref for later disposal call
107         this.muleContext = muleContext;
108         setLifecycleFlags();
109         BindingUtils.configureBinding(component, componentObject);
110     }
111 
112     public DefaultComponentLifecycleAdapter(Object componentObject,
113                                             JavaComponent component,
114                                             FlowConstruct flowConstruct,
115                                             EntryPointResolverSet entryPointResolver, MuleContext muleContext) throws MuleException
116     {
117 
118         this(componentObject, component, flowConstruct, muleContext);
119         this.entryPointResolver = entryPointResolver;
120     }
121 
122     protected void setLifecycleFlags()
123     {
124         Object object = componentObject;
125         initMethod = findInitMethod(object);
126         disposeMethod = findDisposeMethod(object);
127         isInitialisable = initMethod!=null;
128         isDisposable = disposeMethod!=null;
129         isStartable = Startable.class.isInstance(object);
130         isStoppable = Stoppable.class.isInstance(object);
131     }
132 
133     protected Method findInitMethod(Object object)
134     {
135         if(object instanceof Initialisable)
136         {
137             try
138             {
139                 return object.getClass().getMethod(Initialisable.PHASE_NAME);
140             }
141             catch (NoSuchMethodException e)
142             {
143                 //ignore
144             }
145         }
146 
147         List<AnnotationMetaData> metaData = AnnotationUtils.getMethodAnnotations(object.getClass(), PostConstruct.class);
148         if (metaData.size() == 0)
149         {
150             return null;
151         }
152         else if(metaData.size() > 1)
153         {
154             throw new IllegalArgumentException(CoreMessages.objectHasMoreThanOnePostConstructAnnotation(object.getClass()).getMessage());
155         }
156         else
157         {
158             Method m = (Method) metaData.get(0).getMember();
159             new JSR250ValidatorProcessor().validateLifecycleMethod(m);
160             return m;
161         }
162     }
163 
164     protected Method findDisposeMethod(Object object)
165     {
166         if (object instanceof Disposable)
167         {
168             try
169             {
170                 return object.getClass().getMethod(Disposable.PHASE_NAME);
171             }
172             catch (NoSuchMethodException e)
173             {
174                 //ignore
175             }
176         }
177 
178         List<AnnotationMetaData> metaData = AnnotationUtils.getMethodAnnotations(object.getClass(), PreDestroy.class);
179         if (metaData.size() == 0)
180         {
181             return null;
182         }
183         else if (metaData.size() > 1)
184         {
185             throw new IllegalArgumentException(CoreMessages.objectHasMoreThanOnePreDestroyAnnotation(object.getClass()).getMessage());
186         }
187         else
188         {
189             Method m = (Method) metaData.get(0).getMember();
190             new JSR250ValidatorProcessor().validateLifecycleMethod(m);
191             return m;
192         }
193     }
194 
195     /**
196      * Propagates initialise() life-cycle to component object implementations if they
197      * implement the mule {@link Initialisable} interface.
198      * <p/>
199      * <b>NOTE:</b> It is up to component implementations to ensure their implementation of
200      * <code>initialise()</code> is thread-safe.
201      */
202     public void initialise() throws InitialisationException
203     {
204         if (isInitialisable)
205         {
206             try
207             {
208                 initMethod.invoke(componentObject);
209             }
210             catch (IllegalAccessException e)
211             {
212                 throw new InitialisationException(e, this);
213             }
214             catch (InvocationTargetException e)
215             {
216                 throw new InitialisationException(e.getTargetException(), this);
217             }
218         }
219     }
220 
221     /**
222      * Propagates start() life-cycle to component object implementations if they
223      * implement the mule {@link Startable} interface. NOT: It is up to component
224      * implementations to ensure their implementation of start() is thread-safe.
225      */
226     public void start() throws MuleException
227     {
228         if (isStartable)
229         {
230             try
231             {
232                 ((Startable) componentObject).start();
233                 started = true;
234             }
235             catch (Exception e)
236             {
237                 throw new DefaultMuleException(CoreMessages.failedToStart("Service: "
238                                                                           + flowConstruct.getName()), e);
239             }
240         }
241         else
242         {
243             started = true;
244         }
245     }
246 
247     /**
248      * Propagates stop() life-cycle to component object implementations if they
249      * implement the mule {@link Stoppable} interface. NOT: It is up to component
250      * implementations to ensure their implementation of stop() is thread-safe.
251      */
252     public void stop() throws MuleException
253     {
254         if (isStoppable)
255         {
256             try
257             {
258                 ((Stoppable) componentObject).stop();
259                 started = false;
260             }
261             catch (Exception e)
262             {
263                 throw new DefaultMuleException(CoreMessages.failedToStop("Service: "
264                                                                          + flowConstruct.getName()), e);
265             }
266         }
267         else
268         {
269             started = false;
270         }
271     }
272 
273     /**
274      * Propagates dispose() life-cycle to component object implementations if they
275      * implement the mule {@link Disposable} interface. NOT: It is up to component
276      * implementations to ensure their implementation of dispose() is thread-safe.
277      */
278     public void dispose()
279     {
280         try
281         {
282             if (isDisposable)
283             {
284                 // make sure we haven't lost the reference to the object
285                 Object o = componentObject;
286                 if (o != null)
287                 {
288                     try
289                     {
290                         disposeMethod.invoke(o);
291                     }
292                     catch (InvocationTargetException e)
293                     {
294                         //unwrap
295                         throw e.getTargetException();
296                     }
297                 }
298             }
299             componentObject = null;
300 
301         }
302         catch (Throwable e)
303         {
304             logger.error("failed to dispose: " + flowConstruct.getName(), e);
305         }
306         disposed = true;
307     }
308 
309     /**
310      * @return true if the service has been started
311      */
312     public boolean isStarted()
313     {
314         return started;
315     }
316 
317     /**
318      * @return whether the service managed by this lifecycle has been disposed
319      */
320     public boolean isDisposed()
321     {
322         return disposed;
323     }
324 
325     public Object invoke(MuleEvent event) throws MuleException
326     {
327         // Invoke method
328         MuleEventContext eventContext = new DefaultMuleEventContext(event);
329         Object result;
330         try
331         {
332             if (componentObject == null)
333             {
334                 throw new ComponentException(MessageFactory.createStaticMessage("componentObject is null"), RequestContext.getEvent(), component);
335             }
336             // Use the overriding entrypoint resolver if one is set
337             if (component.getEntryPointResolverSet() != null)
338             {
339                 result = component.getEntryPointResolverSet().invoke(componentObject, eventContext);
340             }
341             else
342             {
343                 result = entryPointResolver.invoke(componentObject, eventContext);
344             }
345         }
346         catch (Exception e)
347         {
348             throw new ComponentException(RequestContext.getEvent(), component, e);
349         }
350 
351         return result;
352     }
353 }