You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by le...@apache.org on 2002/06/30 13:39:38 UTC

cvs commit: jakarta-avalon-excalibur/microcontainer/src/test/org/apache/excalibur/microcontainer/test LifecycleTestCase.java

leosutic    2002/06/30 04:39:38

  Modified:    microcontainer/src/java/org/apache/excalibur/microcontainer
                        MicroContainerProxy.java
                        MicroContainerException.java MicroContainer.java
                        Handler.java
  Added:       microcontainer/src/java/org/apache/excalibur/microcontainer
                        DecommissionedComponentException.java
               microcontainer/src/test/org/apache/excalibur/microcontainer/test
                        LifecycleTestCase.java
  Log:
  Added support for taking snapshots of the stack at creation and decommissioning of a component.
  
  Revision  Changes    Path
  1.4       +1 -1      jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/MicroContainerProxy.java
  
  Index: MicroContainerProxy.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/MicroContainerProxy.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- MicroContainerProxy.java	27 Jun 2002 14:33:56 -0000	1.3
  +++ MicroContainerProxy.java	30 Jun 2002 11:39:38 -0000	1.4
  @@ -57,7 +57,7 @@
    *
    * @author <a href="mailto:leo.sutic@inspireinfrastructure.com">Leo Sutic</a>
    */
  -public interface MicroContainerProxy 
  +interface MicroContainerProxy 
   {
       /**
        * Create a new instance of the managed component.
  
  
  
  1.3       +1 -3      jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/MicroContainerException.java
  
  Index: MicroContainerException.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/MicroContainerException.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MicroContainerException.java	27 Jun 2002 14:33:49 -0000	1.2
  +++ MicroContainerException.java	30 Jun 2002 11:39:38 -0000	1.3
  @@ -53,8 +53,6 @@
   import java.io.PrintStream;
   import java.io.PrintWriter;
   
  -import org.apache.avalon.framework.CascadingRuntimeException;
  -
   /**
    * Exception indicating a failure between the MicroContainer and its component.
    * <p>
  @@ -104,7 +102,7 @@
           this( throwable.toString(), throwable );
       }
       
  -    private Throwable getCause()
  +    public Throwable getCause()
       {
           return cause;
       }
  
  
  
  1.5       +146 -20   jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/MicroContainer.java
  
  Index: MicroContainer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/MicroContainer.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- MicroContainer.java	27 Jun 2002 14:33:49 -0000	1.4
  +++ MicroContainer.java	30 Jun 2002 11:39:38 -0000	1.5
  @@ -72,19 +72,51 @@
    *                                             .create();
    * </code></pre>
    *
  + * <p>Note that you must call the <code>create()</code> method to actually
  + * obtain the proxy instance.
  + *
    * @author <a href="mailto:leo.sutic@inspireinfrastructure.com">Leo Sutic</a>
    */
   public class MicroContainer {
      
  +    /**
  +     * The default logger.
  +     */
       private static Logger defaultLogger = new NullLogger ();
       
  +    /**
  +     * Flag indicating whether the micro container should trace the lifecycle of
  +     * its components and output messages if they are released twice or not
  +     * at all. See the <code>traceLifecycle()</code> method for more information.
  +     */
  +    private static boolean traceLifecycle = false;
  +    
  +    /**
  +     * Flag indicating whether the reculting component can be shared among several clients.
  +     */
       private boolean sharedInstance = true;
  +    
  +    /**
  +     * Role->Component mappings for the component. See the <code>components()</code>
  +     * method for more information.
  +     */
       private Map components = new HashMap();
  +    
  +    /**
  +     * The main class for the component.
  +     */
       private final Class componentClass;
  +    private final ClassLoader classLoader;
       
  +    /**
  +     * The configuration to give the component.
  +     */
       private Configuration config = null;
  -    private DefaultConfiguration ownConfig = new DefaultConfiguration("", "");
  +    private DefaultConfiguration ownConfig = new DefaultConfiguration( "", "" );
       
  +    /**
  +     * The context to give the component.
  +     */
       private Context context = null;
       private Map contextMap = new HashMap();
       
  @@ -94,18 +126,61 @@
       
       /**
        * Begin creation of a new MicroContainerProxy for the given component class.
  -     * Note that you are not interested in this class, but the object you get when
  -     * calling <code>create()</code>.
  +     * <b>Note that you must call the create() method to actually obtain a MicroContainer.</b>
  +     *
  +     * @param componentClass the component class. It will be loaded using the context class loader.
  +     * @throws ClassNotFoundException if the class was not found.
  +     */
  +    public MicroContainer( String componentClass ) throws ClassNotFoundException
  +    {
  +        this( componentClass, Thread.currentThread().getContextClassLoader() );
  +    }
  +    
  +    /**
  +     * Begin creation of a new MicroContainerProxy for the given component class.
  +     * <b>Note that you must call the create() method to actually obtain a MicroContainer.</b>
  +     *
  +     * @param componentClass the component class.
  +     * @param classLoader    the classLoader to use.
  +     * @throws ClassNotFoundException if the class was not found.
  +     */
  +    public MicroContainer( String componentClass, ClassLoader classLoader ) throws ClassNotFoundException
  +    {
  +        this( classLoader.loadClass( componentClass ), classLoader );
  +    }
  +    
  +    /**
  +     * Begin creation of a new MicroContainerProxy for the given component class.
  +     * <b>Note that you must call the create() method to actually obtain a MicroContainer.</b>
  +     * The thread's context classloader will be used.
  +     *
  +     * @param componentClass the component class.
        */
       public MicroContainer( Class componentClass )
       {
  -        this.componentClass = componentClass;
  +        this( componentClass, Thread.currentThread().getContextClassLoader() );
       }
       
       /**
  +     * Begin creation of a new MicroContainerProxy for the given component class.
  +     * <b>Note that you must call the create() method to actually obtain a MicroContainer.</b>
  +     *
  +     * @param componentClass the component class.
  +     * @param classLoader the class loadet to use.
  +     */
  +    public MicroContainer( Class componentClass, ClassLoader classLoader )
  +    {
  +        this.componentClass = componentClass;
  +        this.classLoader = classLoader;
  +    }
  +        
  +    /**
        * Specifies whether the container should serve up the same instance to
        * all composers requesting an instance. If the component is thread safe,
        * you can set this to true. The default is <code>true</code>.
  +     *
  +     * @param shared flag indicating whether the component instance can be shared
  +     *               among many clients.
        */
       public MicroContainer sharedInstance( boolean shared )
       {
  @@ -116,7 +191,33 @@
       /**
        * Populates the ComponentManager for the component class.
        * Each entry in the <code>components</code> map is assumed to be a
  -     * mapping from String to MicroContainerProxies, obtained via this class.
  +     * mapping from String to a MicroContainerProxy, obtained via this class, or a Map.
  +     * <p>
  +     * If the entry is a Map, it will be considered a ComponentSelector.
  +     * <p>
  +     * For example:
  +     * <p>
  +     * <code>
  +     * <pre>
  +     * ...
  +     *
  +     * HashMap root = new HashMap();
  +     * root.put( "role", component );  // component is accessed by lookup( "role" )
  +     * 
  +     * HashMap multiple = new HashMap();
  +     *
  +     * // componentA is accessed via a component selector with hint "hintA".
  +     * multiple.put( "hintA", componentA ); 
  +     *
  +     * // componentB is accessed via a component selector with hint "hintB".
  +     * multiple.put( "hintB", componentB ); 
  +     *
  +     * // Access ComponentSelector for componentA and componentB via role "anotherRole".
  +     * root.put( "anotherRole", multiple ); 
  +     *
  +     * ...
  +     * </pre>
  +     * </code>
        */
       public MicroContainer components( Map components )
       {
  @@ -297,9 +398,11 @@
       }
       
       /**
  -     * Set the logger for this component.
  +     * Set the logger for this component. The setting only affects this container.
        * Default is the logger set via <code>setDefaultLogger</code>, or a <code>NullLogger</code>
        * if nothing has been set.
  +     *
  +     * @param logger the new logger.
        */
       public MicroContainer logger( Logger logger )
       {
  @@ -310,6 +413,9 @@
       /**
        * Sets a value in the Parameters instance for this component.
        * This will only have an effect if the component implements <code>Parameterizable</code>.
  +     *
  +     * @param name the name of the parameter.
  +     * @param value the value of the parameter.
        */
       public MicroContainer parameter( String name, String value )
       {
  @@ -322,6 +428,8 @@
        * set via <code>setParameter()</code> and any previous Parameter object set
        * via this method. This will only have an effect if the component implements 
        * <code>Parameterizable</code>.
  +     *
  +     * @param parameters the parameters object to give the component.
        */
       public MicroContainer parameters( Parameters parameters )
       {
  @@ -333,7 +441,7 @@
        * Creates a new MicroContainer proxy. The returned object implements all interfaces
        * of the implementation class.
        */
  -    public MicroContainerProxy create() throws MicroContainerException
  +    public Object create() throws MicroContainerException
       {
           try 
           {
  @@ -358,7 +466,7 @@
               return (MicroContainerProxy) Proxy.newProxyInstance(
                   Thread.currentThread().getContextClassLoader(), 
                   proxyInterfaces, 
  -                new Handler (componentClass, logger, components, parameters, config, context, sharedInstance)
  +                new Handler( componentClass, logger, components, parameters, config, context, sharedInstance, traceLifecycle )
                   );
           } 
           catch (Exception e) 
  @@ -368,17 +476,6 @@
       }
       
       /**
  -     * Sets the default logger to use for all components created.
  -     * Default is a <code>NullLogger</code>.
  -     * 
  -     * @param logger the new default logger.
  -     */
  -    public static void setDefaultLogger( Logger logger )
  -    {
  -        defaultLogger = logger;
  -    }
  -    
  -    /**
        * Disposes of a MicroContainer proxy. This method should be called with
        * every proxy obtained via this class once you are done with the component.
        * 
  @@ -399,7 +496,7 @@
        * @param component the component proxy to release. This parameter may be <code>null</code>,
        *                  in which case nothing is done.
        */
  -    public static void release(Object component) 
  +    public static void release( Object component ) 
       {
           if( component == null )
           {
  @@ -410,5 +507,34 @@
           {
               ((MicroContainerProxy) component).MicroContainerProxy__release();
           }
  +    }
  +    
  +    /**
  +     * Sets the default logger to use for all components created.
  +     * Default is a <code>NullLogger</code>.
  +     * 
  +     * @param logger the new default logger.
  +     */
  +    public static void setDefaultLogger( Logger logger )
  +    {
  +        defaultLogger = logger;
  +    }
  +    
  +    /**
  +     * Enabled / disables tracing of component lifecycle. If enabled, a stack snapshot will
  +     * be taken when a proxy is created, and one when it is released. If the proxy is released
  +     * multiple times, the snapshot will be printed to <code>System.err</code> with the message "proxy was
  +     * released at..." and a snapshot of the current stack. If the proxy isn't released - its <code>finalize()</code> 
  +     * method is called before <code>release()</code> - a message will be printed to System.err along with the stack 
  +     * snapshot taken when the component was created. The method will only have effect on components created after it 
  +     * was called.
  +     * <p>
  +     * By default, tracing is disabled.
  +     *
  +     * @param trace flag indicating whether tracing is enabled or disabled
  +     */
  +    public static void traceLifecycle( boolean trace )
  +    {
  +        traceLifecycle = trace;
       }
   }
  
  
  
  1.5       +87 -4     jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/Handler.java
  
  Index: Handler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/Handler.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- Handler.java	27 Jun 2002 14:34:05 -0000	1.4
  +++ Handler.java	30 Jun 2002 11:39:38 -0000	1.5
  @@ -125,8 +125,40 @@
        */
       private final boolean shared;
       
  -    public Handler (Class componentClass, Logger logger, Map components, Parameters parameters, Configuration configuration, Context context, boolean shared) 
  +    /**
  +     * Flag indicating whether this component has been released.
  +     */
  +    private boolean released;
  +    
  +    /**
  +     * Flag indicating whether this proxy should take stack snapshots when
  +     * it is created and released.
  +     */
  +    private boolean traceLifecycle;
  +        
  +    /**
  +     * Snapshot of the stack as it were when this proxy was released.
  +     * This is only set if the traceLifecycle parameter to the constructor is
  +     * true.
  +     */
  +    private Throwable releasedAt;
  +    
  +    /**
  +     * Snapshot of the stack as it were when this proxy was created.
  +     * This is only set if the traceLifecycle parameter to the constructor is
  +     * true.
  +     */
  +    private Throwable createdAt;
  +    
  +    private String snapshotMessage = " ***This is a stack snapshot taken because you set " + 
  +        "MicroContainer.traceLifeCycle( true ) - this exception has not been thrown, although this " + 
  +        "exception may appear as a cause of another exception (such as DecommissionedComponentException).***";
  +    
  +    public Handler( Class componentClass, Logger logger, Map components, Parameters parameters, 
  +        Configuration configuration, Context context, boolean shared, boolean traceLifecycle) 
       {
  +        this.traceLifecycle = traceLifecycle;
  +        
           this.componentClass = componentClass;
           
           if( Composable.class.isAssignableFrom( componentClass ) )
  @@ -153,16 +185,49 @@
           this.parameters = parameters;
           this.logger = logger;
           
  +        if( traceLifecycle )
  +        {
  +            createdAt = new Exception( "Stack snapshot for creation of " + componentClass.getName() + "." + snapshotMessage );
  +        }
  +        
           this.instance = newInstance ();
       }
       
  +    public void finalize()
  +    {
  +        if( !released )
  +        {
  +            MicroContainerProxy__release();
  +            if( traceLifecycle )
  +            {
  +                System.err.println( "This proxy was never released. This is a snapshot of the stack as it was when it was created:" );
  +                createdAt.printStackTrace( System.err );
  +            }
  +        }
  +    }
  +    
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
       {
  -        if (method.getDeclaringClass() == MicroContainerProxy.class)
  +        if( method.getDeclaringClass() == MicroContainerProxy.class )
           {
               return method.invoke( this, args );
           }
  -        return method.invoke( instance, args );
  +        
  +        if( !released )
  +        {
  +            return method.invoke( instance, args );
  +        }
  +        else
  +        {
  +            if( traceLifecycle )
  +            {
  +                throw new DecommissionedComponentException( releasedAt );
  +            }
  +            else
  +            {
  +                throw new DecommissionedComponentException();
  +            }
  +        }
       } 
       
       private Object newInstance () throws MicroContainerException
  @@ -321,6 +386,24 @@
       
       public void MicroContainerProxy__release() 
       {
  -        decommission( instance );
  +        if( !released )
  +        {
  +            if( traceLifecycle )
  +            {
  +                releasedAt = new Exception( "Stack snapshot for release of " + componentClass.getName() + "." + snapshotMessage );
  +            }
  +            decommission( instance );
  +            released = true;
  +        }
  +        else
  +        {
  +            if( traceLifecycle )
  +            {
  +                System.err.println( "This proxy has already been released. This is a snapshot of the stack as it was when it was first released:" );
  +                releasedAt.printStackTrace( System.err );
  +                System.err.println( "This is a snapshot of the stack as it is now:" );
  +                new Exception( "Current stack snapshot for " + componentClass.getName() + "." + snapshotMessage ).printStackTrace( System.err );
  +            }
  +        }
       }
   }
  
  
  
  1.1                  jakarta-avalon-excalibur/microcontainer/src/java/org/apache/excalibur/microcontainer/DecommissionedComponentException.java
  
  Index: DecommissionedComponentException.java
  ===================================================================
  /*
  
  ============================================================================
  The Apache Software License, Version 1.1
  ============================================================================
  
  Copyright (C) 2002 The Apache Software Foundation. All rights reserved.
  
  Redistribution and use in source and binary forms, with or without modifica-
  tion, are permitted provided that the following conditions are met:
  
  1. Redistributions of  source code must  retain the above copyright  notice,
  this list of conditions and the following disclaimer.
  
  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
  
  3. The end-user documentation included with the redistribution, if any, must
  include  the following  acknowledgment:  "This product includes  software
  developed  by the  Apache Software Foundation  (http://www.apache.org/)."
  Alternately, this  acknowledgment may  appear in the software itself,  if
  and wherever such third-party acknowledgments normally appear.
  
  4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"  
  must not be used to endorse or promote products derived from this  software 
  without  prior written permission. For written permission, please contact 
  apache@apache.org.
  
  5. Products  derived from this software may not  be called "Apache", nor may
  "Apache" appear  in their name,  without prior written permission  of the
  Apache Software Foundation.
  
  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
  APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
  INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
  DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
  OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
  ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
  (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  This software  consists of voluntary contributions made  by many individuals
  on  behalf of the Apache Software  Foundation. For more  information on the 
  Apache Software Foundation, please see <http://www.apache.org/>.
  
  */
  package org.apache.excalibur.microcontainer;
  
  /**
   * Thrown when a method is invoked on a component that is
   * decommissioned.
   */
  public class DecommissionedComponentException extends MicroContainerException {
      
      public DecommissionedComponentException( )
      {
          super( "Component already decommissioned." );
      }
      
      public DecommissionedComponentException( Throwable snapshot )
      {
          super( "Component already decommissioned. ", snapshot );
      }
  
      /**
       * Returns a stack snapshot in the form of a throwable, taken when the component
       * was decommissioned.
       */
      public Throwable getSnapshot() {
          return getCause();
      }
  }
  
  
  
  1.1                  jakarta-avalon-excalibur/microcontainer/src/test/org/apache/excalibur/microcontainer/test/LifecycleTestCase.java
  
  Index: LifecycleTestCase.java
  ===================================================================
  /*
  
  ============================================================================
  The Apache Software License, Version 1.1
  ============================================================================
  
  Copyright (C) 2002 The Apache Software Foundation. All rights reserved.
  
  Redistribution and use in source and binary forms, with or without modifica-
  tion, are permitted provided that the following conditions are met:
  
  1. Redistributions of  source code must  retain the above copyright  notice,
  this list of conditions and the following disclaimer.
  
  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
  
  3. The end-user documentation included with the redistribution, if any, must
  include  the following  acknowledgment:  "This product includes  software
  developed  by the  Apache Software Foundation  (http://www.apache.org/)."
  Alternately, this  acknowledgment may  appear in the software itself,  if
  and wherever such third-party acknowledgments normally appear.
  
  4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation"  
  must not be used to endorse or promote products derived from this  software 
  without  prior written permission. For written permission, please contact 
  apache@apache.org.
  
  5. Products  derived from this software may not  be called "Apache", nor may
  "Apache" appear  in their name,  without prior written permission  of the
  Apache Software Foundation.
  
  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
  APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
  INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
  DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
  OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
  ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
  (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  This software  consists of voluntary contributions made  by many individuals
  on  behalf of the Apache Software  Foundation. For more  information on the 
  Apache Software Foundation, please see <http://www.apache.org/>.
  
  */
  
  package org.apache.excalibur.microcontainer.test;
  
  import junit.framework.TestCase;
  
  import org.apache.excalibur.microcontainer.*;
  
  /**
   * Tests access of components lifecycle.
   *
   * @author <a href="mailto:leo.sutic@inspireinfrastructure.com">Leo Sutic</a>
   */ 
  public class LifecycleTestCase extends TestCase
  {
      
      public LifecycleTestCase(String name) 
      {
          super(name);
      }
      
      public void testDoubleRelease() throws Exception 
      {
          MicroContainer.traceLifecycle( true );
          
          Supplier supplier1 = SupplierImpl.getInstance (1);
          
          MicroContainer.release( supplier1 );
          
          // This will cause a lot of stuff to be printed to System.err.
          MicroContainer.release( supplier1 );        
      }    
      
      public void testAccessAfterRelease() throws Exception 
      {
          MicroContainer.traceLifecycle( true );
          
          Supplier supplier1 = SupplierImpl.getInstance (1);
          
          MicroContainer.release( supplier1 );
          
          try 
          {
              supplier1.getId();
              fail( "DecommissionedComponentException not thrown although a method was called on a release'd component." );
          }
          catch( DecommissionedComponentException de )
          {
          }        
      }
  }
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>