View Javadoc

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