Coverage Report - org.mule.component.DefaultComponentLifecycleAdapter
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultComponentLifecycleAdapter
0%
0/125
0%
0/46
0
 
 1  
 /*
 2  
  * $Id: DefaultComponentLifecycleAdapter.java 20486 2010-12-07 11:12:25Z dirk.olmes $
 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  0
     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  0
     protected boolean isInitialisable = false;
 83  0
     protected boolean isStartable = false;
 84  0
     protected boolean isStoppable = false;
 85  0
     protected boolean isDisposable = false;
 86  
 
 87  
     protected Method initMethod;
 88  
     protected Method disposeMethod;
 89  
 
 90  0
     private boolean started = false;
 91  0
     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  0
     {
 100  0
         if (muleContext == null)
 101  
         {
 102  0
             throw new IllegalStateException("No muleContext provided");
 103  
         }
 104  0
         if (componentObject == null)
 105  
         {
 106  0
             throw new IllegalArgumentException("POJO Service cannot be null");
 107  
         }
 108  
 
 109  0
         if (entryPointResolver == null)
 110  
         {
 111  0
             entryPointResolver = new LegacyEntryPointResolverSet();
 112  
         }
 113  0
         this.componentObject = componentObject;
 114  0
         this.component = component;
 115  0
         this.flowConstruct = flowConstruct;
 116  
 
 117  
         // save a ref for later disposal call
 118  0
         this.muleContext = muleContext;
 119  0
         setLifecycleFlags();
 120  0
         configureBinding();
 121  0
     }
 122  
 
 123  
     public DefaultComponentLifecycleAdapter(Object componentObject,
 124  
                                             JavaComponent component,
 125  
                                             FlowConstruct flowConstruct,
 126  
                                             EntryPointResolverSet entryPointResolver, MuleContext muleContext) throws MuleException
 127  
     {
 128  
 
 129  0
         this(componentObject, component, flowConstruct, muleContext);
 130  0
         this.entryPointResolver = entryPointResolver;
 131  0
     }
 132  
 
 133  
     protected void setLifecycleFlags()
 134  
     {
 135  0
         Object object = componentObject;
 136  0
         initMethod = findInitMethod(object);
 137  0
         disposeMethod = findDisposeMethod(object);
 138  0
         isInitialisable = initMethod!=null;
 139  0
         isDisposable = disposeMethod!=null;
 140  0
         isStartable = Startable.class.isInstance(object);
 141  0
         isStoppable = Stoppable.class.isInstance(object);
 142  0
     }
 143  
 
 144  
     protected Method findInitMethod(Object object)
 145  
     {
 146  0
         if(object instanceof Initialisable)
 147  
         {
 148  
             try
 149  
             {
 150  0
                 return object.getClass().getMethod(Initialisable.PHASE_NAME);
 151  
             }
 152  0
             catch (NoSuchMethodException e)
 153  
             {
 154  
                 //ignore
 155  
             }
 156  
         }
 157  
 
 158  0
         List<AnnotationMetaData> metaData = AnnotationUtils.getMethodAnnotations(object.getClass(), PostConstruct.class);
 159  0
         if (metaData.size() == 0)
 160  
         {
 161  0
             return null;
 162  
         }
 163  0
         else if(metaData.size() > 1)
 164  
         {
 165  0
             throw new IllegalArgumentException(CoreMessages.objectHasMoreThanOnePostConstructAnnotation(object.getClass()).getMessage());
 166  
         }
 167  
         else
 168  
         {
 169  0
             Method m = (Method) metaData.get(0).getMember();
 170  0
             new JSR250ValidatorProcessor().validateLifecycleMethod(m);
 171  0
             return m;
 172  
         }
 173  
     }
 174  
 
 175  
     protected Method findDisposeMethod(Object object)
 176  
     {
 177  0
         if (object instanceof Disposable)
 178  
         {
 179  
             try
 180  
             {
 181  0
                 return object.getClass().getMethod(Disposable.PHASE_NAME);
 182  
             }
 183  0
             catch (NoSuchMethodException e)
 184  
             {
 185  
                 //ignore
 186  
             }
 187  
         }
 188  
 
 189  0
         List<AnnotationMetaData> metaData = AnnotationUtils.getMethodAnnotations(object.getClass(), PreDestroy.class);
 190  0
         if (metaData.size() == 0)
 191  
         {
 192  0
             return null;
 193  
         }
 194  0
         else if (metaData.size() > 1)
 195  
         {
 196  0
             throw new IllegalArgumentException(CoreMessages.objectHasMoreThanOnePreDestroyAnnotation(object.getClass()).getMessage());
 197  
         }
 198  
         else
 199  
         {
 200  0
             Method m = (Method) metaData.get(0).getMember();
 201  0
             new JSR250ValidatorProcessor().validateLifecycleMethod(m);
 202  0
             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  0
         if (isInitialisable)
 216  
         {
 217  
             try
 218  
             {
 219  0
                 initMethod.invoke(componentObject);
 220  
             }
 221  0
             catch (IllegalAccessException e)
 222  
             {
 223  0
                 throw new InitialisationException(e, this);
 224  
             }
 225  0
             catch (InvocationTargetException e)
 226  
             {
 227  0
                 throw new InitialisationException(e.getTargetException(), this);
 228  0
             }
 229  
         }
 230  0
     }
 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  0
         if (isStartable)
 240  
         {
 241  
             try
 242  
             {
 243  0
                 ((Startable) componentObject).start();
 244  0
                 started = true;
 245  
             }
 246  0
             catch (Exception e)
 247  
             {
 248  0
                 throw new DefaultMuleException(CoreMessages.failedToStart("Service: "
 249  
                                                                           + flowConstruct.getName()), e);
 250  0
             }
 251  
         }
 252  
         else
 253  
         {
 254  0
             started = true;
 255  
         }
 256  0
     }
 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  0
         if (isStoppable)
 266  
         {
 267  
             try
 268  
             {
 269  0
                 ((Stoppable) componentObject).stop();
 270  0
                 started = false;
 271  
             }
 272  0
             catch (Exception e)
 273  
             {
 274  0
                 throw new DefaultMuleException(CoreMessages.failedToStop("Service: "
 275  
                                                                          + flowConstruct.getName()), e);
 276  0
             }
 277  
         }
 278  
         else
 279  
         {
 280  0
             started = false;
 281  
         }
 282  0
     }
 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  0
             if (isDisposable)
 294  
             {
 295  
                 // make sure we haven't lost the reference to the object
 296  0
                 Object o = componentObject;
 297  0
                 if (o != null)
 298  
                 {
 299  
                     try
 300  
                     {
 301  0
                         disposeMethod.invoke(o);
 302  
                     }
 303  0
                     catch (InvocationTargetException e)
 304  
                     {
 305  
                         //unwrap
 306  0
                         throw e.getTargetException();
 307  0
                     }
 308  
                 }
 309  
             }
 310  0
             componentObject = null;
 311  
 
 312  
         }
 313  0
         catch (Throwable e)
 314  
         {
 315  0
             logger.error("failed to dispose: " + flowConstruct.getName(), e);
 316  0
         }
 317  0
         disposed = true;
 318  0
     }
 319  
 
 320  
     /**
 321  
      * @return true if the service has been started
 322  
      */
 323  
     public boolean isStarted()
 324  
     {
 325  0
         return started;
 326  
     }
 327  
 
 328  
     /**
 329  
      * @return whether the service managed by this lifecycle has been disposed
 330  
      */
 331  
     public boolean isDisposed()
 332  
     {
 333  0
         return disposed;
 334  
     }
 335  
 
 336  
     public Object invoke(MuleEvent event) throws MuleException
 337  
     {
 338  
         // Invoke method
 339  0
         MuleEventContext eventContext = new DefaultMuleEventContext(event);
 340  
         Object result;
 341  
         try
 342  
         {
 343  0
             if (componentObject == null)
 344  
             {
 345  0
                 throw new ComponentException(MessageFactory.createStaticMessage("componentObject is null"), RequestContext.getEvent(), component);
 346  
             }
 347  
             // Use the overriding entrypoint resolver if one is set
 348  0
             if (component.getEntryPointResolverSet() != null)
 349  
             {
 350  0
                 result = component.getEntryPointResolverSet().invoke(componentObject, eventContext);
 351  
             }
 352  
             else
 353  
             {
 354  0
                 result = entryPointResolver.invoke(componentObject, eventContext);
 355  
             }
 356  
         }
 357  0
         catch (Exception e)
 358  
         {
 359  0
             throw new ComponentException(RequestContext.getEvent(), component, e);
 360  0
         }
 361  
 
 362  0
         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  0
         if (component.getInterfaceBindings() != null)
 370  
         {
 371  0
             Map<Class<?>, Object> bindings = new HashMap<Class<?>, Object>();
 372  0
             for (InterfaceBinding interfaceBinding : component.getInterfaceBindings())
 373  
             {
 374  0
                 Object proxy = bindings.get(interfaceBinding.getInterface());
 375  
 
 376  0
                 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  0
                     proxy = interfaceBinding.createProxy(componentObject);
 383  0
                     bindings.put(interfaceBinding.getInterface(), proxy);
 384  
 
 385  
                     // Now lets set the proxy on the Service object
 386  
                     Method setterMethod;
 387  
 
 388  0
                     List methods = ClassUtils.getSatisfiableMethods(componentObject.getClass(),
 389  
                             new Class[]{interfaceBinding.getInterface()}, true, false, null);
 390  0
                     if (methods.size() == 1)
 391  
                     {
 392  0
                         setterMethod = (Method) methods.get(0);
 393  
                     }
 394  0
                     else if (methods.size() > 1)
 395  
                     {
 396  0
                         throw new TooManySatisfiableMethodsException(componentObject.getClass(),
 397  
                                 new Class[]{interfaceBinding.getInterface()});
 398  
                     }
 399  
                     else
 400  
                     {
 401  0
                         throw new NoSatisfiableMethodsException(componentObject.getClass(),
 402  
                                 new Class[]{interfaceBinding.getInterface()});
 403  
                     }
 404  
 
 405  
                     try
 406  
                     {
 407  0
                         setterMethod.invoke(componentObject, proxy);
 408  
                     }
 409  0
                     catch (Exception e)
 410  
                     {
 411  0
                         throw new InitialisationException(CoreMessages.failedToSetProxyOnService(interfaceBinding,
 412  
                                 componentObject.getClass()), e, this);
 413  0
                     }
 414  0
                 }
 415  
                 else
 416  
                 {
 417  0
                     BindingInvocationHandler handler = (BindingInvocationHandler) Proxy.getInvocationHandler(proxy);
 418  0
                     handler.addRouterForInterface(interfaceBinding);
 419  
                 }
 420  0
             }
 421  
         }
 422  0
     }
 423  
 }