avalon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Leif Mortenson <l...@silveregg.co.jp>
Subject Re: [PATCH] ExcaliburComponentManager patch
Date Tue, 19 Feb 2002 08:02:19 GMT
This problem fixes some problems that we have been having with the 
current ExcalibutComponentManager, and includes tests.  So if nobody 
finds any problems testing the patch, I will go ahead and commit these 
changes in the morning.

Cheers,
Leif

Ryan Shaw wrote:

>The changes to PoolableComponentHandler (so it uses ResourceLimitingPool)
>broke the unit test suite I posted earlier, since Component instances are
>now not created until they are needed (thus the expected number of 
>Component disposals is different).
>
>I have attached the updated test suite, which works with these latest
>changes.
>
>Ryan
>
>Ryan wrote:
>
>||| I haven't been following the "ComponentManager Interface" thread in its
>||| entirety, as I occasionally like to step away from my computer for things
>||| like food and sleep. :^)
>||| 
>||| But I did notice in skimming through some of the posts that you guys were
>||| discussing Peter's "releaseless" CM. I have tried to implement that in this
>||| patch. The CM allows Composables to release components they have looked up
>||| but does not require this--it will release components for them.
>||| 
>||| Note, however, that classes with "direct" access to the CM--i.e. classes
>||| that did not obtain references to the CM via a "compose()" method--must
>||| release their components, or else warnings will be logged when those
>||| components are disposed. This is because the resource tracking is done,
>||| per Peter's suggestion, via proxy ComponentManagers provided to Composables.
>||| Classes with "direct" access do not have these proxies so their lookups
>||| are not tracked.
>||| 
>||| That also means that in a situation where classes with direct ECM access
>||| do not release their components, correct ordering of disposal is currently
>||| not guaranteed. I can think of some ways to fix this (like having the ECM
>||| keep track of "direct" lookups so it knows which "top-level" components
>||| to dispose of first in order to kick off the component disposal process)
>||| but they aren't in this patch.
>||| 
>||| Ryan
>||| 
>||| Ryan wrote:
>||| 
>||| ||| Attached is a patch and unit test suite for some changes I
>||| ||| needed to fix problems I was having with the ECM.
>||| ||| 
>||| ||| Specifically:
>||| ||| 
>||| ||| 1) Components being disposed before other components finished using them.
>||| ||| 2) SingleThreaded Components never being disposed at all.
>||| ||| 
>||| ||| You can run the unit test suite with the command:
>||| ||| 
>||| ||| ./build.sh -Djunit.test=**/ExcaliburComponentManagerTestCase.class test-subset
>||| ||| 
>||| ||| The test suite sets up an ECM with combinations of Components of various
>||| ||| lifestyles (SingleThreaded, ThreadSafe, Poolable) and behaviors (proper
>||| ||| releasing of other components or not) and checks to see if the two problems
>||| ||| mentioned above are occurring.
>||| ||| 
>||| ||| Please check it out and let me know if you see any problems.
>||| ||| 
>||| ||| Thanks,
>||| ||| 
>||| ||| Ryan
>||| 
>||| --
>||| To unsubscribe, e-mail:   <mailto:avalon-dev-unsubscribe@jakarta.apache.org>
>||| For additional commands, e-mail: <mailto:avalon-dev-help@jakarta.apache.org>
>||| 
>||| 
>
>
>------------------------------------------------------------------------
>
>/*
> * Copyright (C) The Apache Software Foundation. All rights reserved.
> *
> * This software is published under the terms of the Apache Software License
> * version 1.1, a copy of which has been included with this distribution in
> * the LICENSE.txt file.
> */
>package org.apache.avalon.excalibur.component.test;
>
>import org.apache.avalon.excalibur.pool.Poolable;
>import org.apache.avalon.excalibur.component.DefaultComponentPool;
>import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
>import org.apache.avalon.excalibur.util.ComponentStateValidator;
>
>import org.apache.avalon.framework.logger.Logger;
>import org.apache.avalon.framework.logger.AbstractLogEnabled;
>import org.apache.avalon.framework.context.DefaultContext;
>import org.apache.avalon.framework.configuration.DefaultConfiguration;
>import org.apache.avalon.framework.configuration.Configuration;
>import org.apache.avalon.framework.component.Component;
>import org.apache.avalon.framework.component.Composable;
>import org.apache.avalon.framework.component.ComponentManager;
>import org.apache.avalon.framework.component.ComponentException;
>import org.apache.avalon.framework.activity.Disposable;
>import org.apache.avalon.framework.thread.ThreadSafe;
>import org.apache.avalon.framework.thread.SingleThreaded;
>
>import junit.framework.TestSuite;
>import junit.framework.TestCase;
>
>import org.apache.log.Hierarchy;
>import org.apache.log.Priority;
>import org.apache.log.LogTarget;
>import org.apache.log.format.PatternFormatter;
>import org.apache.log.output.io.StreamTarget;
>
>/**
> * This class is for testing the ExcaliburComponentManager to verify that
> * it is correctly handling component lifestyle management.
> *
> * @author <a href="mailto:ryan@silveregg.co.jp">Ryan Shaw</a>
> * @version $Revision$
> */
>public class ExcaliburComponentManagerTestCase extends TestCase 
>{
>    /**
>     * Here we create a suite lots of tests to test the interactions of 
>     * various types of components. Basically there are three Roles 
>     * involved: Mom, Dad, and Kid. Each of the three Roles can be 
>     * implemented by a SingleThreaded, ThreadSafe, or Poolable component.
>     * The Mom and Dad components both are Composable, and they use the
>     * ComponentManager that they are provided with to obtain references
>     * to a Kid component. The Mom and Dad components may be "Good" (they
>     * properly release their Kid) or "Bad" (they don't release their Kid).
>     *
>     * Each of the tests sets up a different combo of these component
>     * implementations and checks to make sure that everything gets disposed,
>     * and that Kids never get disposed before parents are done using them.
>     *
>     * @return a <code>TestSuite</code>
>     */
>    public static TestSuite suite()
>    {
>        TestSuite suite = new TestSuite();
>        
>        String[] behaviors  = { "Bad", "Good" }; 
>        String[] kidTypes   = { "" }; // , "BadCircular", "GoodCircular" };
>        String[] lifestyles = { "SingleThreaded" , "ThreadSafe", "Poolable" }; 
>
>        for ( int mb = 0; mb < behaviors.length; mb++ )
>        {
>            for ( int db = 0; db < behaviors.length; db++ )
>            {
>                for ( int kt = 0; kt < kidTypes.length; kt++ )
>                {
>                    for ( int ml = 0; ml < lifestyles.length; ml++ ) 
>                    {
>                        for ( int dl = 0; dl < lifestyles.length; dl++ ) 
>                        {
>                            for ( int kl = 0; kl < lifestyles.length; kl++ ) 
>                            {
>                                final String momClassName = 
>                                    lifestyles[ ml ] + behaviors[ mb ] + "Mom";
>                                final String dadClassName = 
>                                    lifestyles[ dl ] + behaviors[ db ] + "Dad";
>                                final String kidClassName = 
>                                    lifestyles[ kl ] +  kidTypes[ kt ] + "Kid";
>                                
>                                final String prefix = 
>                                    ExcaliburComponentManagerTestCase.class.getName()
+ "$";
>                                
>                                suite.addTest
>                                    ( new ExcaliburComponentManagerTestCase( momClassName
+ 
>                                                                             dadClassName
+ 
>                                                                             kidClassName
) 
>                                        {
>                                            public void runTest() throws Exception 
>                                            {
>                                                managerLifecycle( Class.forName
>                                                                  ( prefix + momClassName
), 
>                                                                  Class.forName
>                                                                  ( prefix + dadClassName
),
>                                                                  Class.forName
>                                                                  ( prefix + kidClassName
) );
>                                            }
>                                        }
>                                      );
>                            }
>                        }
>                    }
>                }
>            }
>        }
>        
>        return suite;
>    }
>
>    private void managerLifecycle(Class momClass, Class dadClass, Class kidClass) 
>        throws Exception
>    {
>        Configuration emptyConfig = new DefaultConfiguration( "", "" );
>        
>        m_manager.addComponent( Mom.ROLE, momClass, emptyConfig );
>        m_manager.addComponent( Dad.ROLE, dadClass, emptyConfig );
>        m_manager.addComponent( Kid.ROLE, kidClass, emptyConfig );
>        
>        m_manager.initialize();
>        
>        Component mom = m_manager.lookup( Mom.ROLE );
>        Component dad = m_manager.lookup( Dad.ROLE );
>
>        m_manager.release( mom );
>        m_manager.release( dad );
>        
>        m_manager.dispose();
>
>	checkNumberOfDisposals( momClass, dadClass, kidClass );
>    }
>    
>    private void checkNumberOfDisposals( Class momClass, Class dadClass, Class kidClass
) 
>    {
>        int momInstances = 1, dadInstances = 1;
>        
>	int kidInstances = determineNumberOfKidInstances( kidClass, momInstances, dadInstances
);
>        
>        int expectedDisposals = momInstances + dadInstances + kidInstances;
>        
>        assertEquals( expectedDisposals, m_disposals ); 
>    }
>
>    private int determineNumberOfKidInstances( Class kidClass, int momInstances, int dadInstances
) 
>    {
>        int parentInstances = ( momInstances + dadInstances );
>        
>        if ( ThreadSafe.class.isAssignableFrom( kidClass ) )
>        { 
>            // parents share reference to same kid instance
>            return 1;
>        } 
>        else if ( Poolable.class.isAssignableFrom( kidClass ) )
>        {
>            int poolGrowParameter = DefaultComponentPool.DEFAULT_POOL_SIZE / 4;
>
>            int extraKidsNeeded = parentInstances % poolGrowParameter;
>            
>            if ( extraKidsNeeded > 0 )
>            {
>                // kid pool will grow to feed parents
>                return parentInstances + ( poolGrowParameter - extraKidsNeeded );
>            }
>        }
>
>        // each parent has a single kid reference
>        return parentInstances;
>    }
>
>    /* ======================================================================== *
>     *                           Test Components.                               *
>     * ======================================================================== */
>
>    public static abstract class AbstractBadParent extends AbstractLogEnabled
>        implements Component, Composable, Disposable
>    {
>        private final ComponentStateValidator m_validator = new ComponentStateValidator(
this );
>        
>        protected ComponentManager m_manager;
>        protected Kid              m_kid;
>        
>        public void enableLogging( Logger logger )
>        {
>            m_validator.checkLogEnabled();
>            
>            super.enableLogging( logger );
>        }
>        
>        public void compose( ComponentManager manager ) throws ComponentException
>        {
>            m_validator.checkComposed();
>
>            m_manager = manager;
>            
>            m_kid = (Kid) m_manager.lookup( Kid.ROLE );
>        }
>
>        public void dispose()
>        {
>            m_validator.checkDisposed();
>
>            try
>            {
>                m_kid.getName();
>            } 
>            catch ( IllegalStateException ise )
>            {
>                fail( ise.getMessage() );
>            }
>            
>            m_disposals++;
>        }
>    }
>
>    public static abstract class AbstractGoodParent extends AbstractBadParent
>    {
>        public void dispose()
>        {
>            super.dispose();
>            m_manager.release( m_kid );
>        }
>    }
>    
>    public interface Mom extends Component
>    {
>        String ROLE = "Mom";
>    }
>    
>    public static class SingleThreadedBadMom extends AbstractBadParent
>        implements Mom, SingleThreaded
>    {}
>    public static class SingleThreadedGoodMom extends AbstractGoodParent
>        implements Mom, SingleThreaded
>    {}
>    public static class ThreadSafeBadMom extends AbstractBadParent
>        implements Mom, ThreadSafe
>    {}
>    public static class ThreadSafeGoodMom extends AbstractGoodParent
>        implements Mom, ThreadSafe
>    {}
>    public static class PoolableBadMom extends AbstractBadParent
>        implements Mom, Poolable
>    {}
>    public static class PoolableGoodMom extends AbstractGoodParent
>        implements Mom, Poolable
>    {}
>
>    public interface Dad extends Component
>    {
>        String ROLE = "Dad";
>    }
>    
>    public static class SingleThreadedBadDad extends AbstractBadParent
>        implements Dad, SingleThreaded
>    {}
>    public static class SingleThreadedGoodDad extends AbstractGoodParent
>        implements Dad, SingleThreaded
>    {}
>    public static class ThreadSafeBadDad extends AbstractBadParent
>        implements Dad, ThreadSafe
>    {}
>    public static class ThreadSafeGoodDad extends AbstractGoodParent
>        implements Dad, ThreadSafe
>    {}
>    public static class PoolableBadDad extends AbstractBadParent
>        implements Dad, Poolable
>    {}
>    public static class PoolableGoodDad extends AbstractGoodParent
>        implements Dad, Poolable
>    {}
>    
>    public interface Kid extends Component
>    {
>        String ROLE = "Kid";
>
>        String getName();
>    }
>    
>    public static abstract class AbstractKid extends AbstractLogEnabled
>        implements Kid, Disposable
>    {
>        public final static String ROLE = "Kid";
>
>        protected final ComponentStateValidator m_validator = new ComponentStateValidator(
this );
>
>        public void enableLogging( Logger logger )
>        {
>            m_validator.checkLogEnabled();
>
>            super.enableLogging( logger );
>        }
>
>        public void dispose()
>        {
>            m_validator.checkDisposed();
>
>            m_disposals++;
>        }
>
>        public String getName()
>        {
>            m_validator.checkActive();
>            
>            return "Kid";
>        } 
>    }
>
>    public static class SingleThreadedKid extends AbstractKid
>        implements SingleThreaded
>    {}
>    public static class ThreadSafeKid extends AbstractKid
>        implements ThreadSafe
>    {}
>    public static class PoolableKid extends AbstractKid
>        implements Poolable
>    {}
>    
>    public static abstract class AbstractBadCircularKid extends AbstractKid
>        implements Composable
>    {
>        protected ComponentManager m_manager;
>        protected Mom              m_mom;
>        protected Dad              m_dad;
>        
>        public void compose( ComponentManager manager ) throws ComponentException
>        {
>            m_validator.checkComposed();
>            
>            m_manager = manager;
>        }
>
>        public String getName()
>        {
>            String name = super.getName();
>            
>            try 
>            {
>                m_mom = (Mom) m_manager.lookup( Mom.ROLE );
>                m_dad = (Dad) m_manager.lookup( Dad.ROLE );
>            }
>            catch ( ComponentException ce )
>            {
>                fail( ce.getMessage() );
>            }
>
>            return ( name + " belongs to " + m_mom + " and " + m_dad );
>        }
>    }
>
>    public static abstract class AbstractGoodCircularKid extends AbstractBadCircularKid
>    {
>        public void dispose()
>        {
>            super.dispose();
>            
>            m_manager.release( m_mom );
>            m_manager.release( m_dad );
>        }
>    }
>
>    public static class SingleThreadedBadCircularKid extends AbstractBadCircularKid
>        implements SingleThreaded
>    {}
>    public static class ThreadSafeBadCircularKid extends AbstractBadCircularKid
>        implements ThreadSafe
>    {}
>    public static class PoolableBadCircularKid extends AbstractBadCircularKid
>        implements Poolable
>    {}
>    public static class SingleThreadedGoodCircularKid extends AbstractGoodCircularKid
>        implements SingleThreaded
>    {}
>    public static class ThreadSafeGoodCircularKid extends AbstractGoodCircularKid
>        implements ThreadSafe
>    {}
>    public static class PoolableGoodCircularKid extends AbstractGoodCircularKid
>        implements Poolable
>    {}
>    
>    /* ======================================================================== *
>     *                           Housekeeping.                                  *
>     * ======================================================================== */
>    
>    private static int m_disposals;
>    
>    private ExcaliburComponentManager m_manager;
>    
>    public ExcaliburComponentManagerTestCase( String name ) 
>    {
>        super( name );
>    }
>
>    public void setUp() throws Exception
>    {
>        m_disposals = 0;
>
>        m_manager = new ExcaliburComponentManager();
>        
>        final String pattern = 
>            ( "%5.5{priority} [%40.40{category}]: %{message}\n%{throwable}" );
>        
>        org.apache.log.Logger logger = Hierarchy.getDefaultHierarchy().getLoggerFor( getName()
);
>        logger.setLogTargets
>            ( new LogTarget[] 
>            { new StreamTarget( System.out, new PatternFormatter( pattern ) ) } );
>        logger.setPriority( Priority.INFO );
>        
>        m_manager.setLogger( logger );
>        m_manager.contextualize( new DefaultContext() );
>        m_manager.configure( new DefaultConfiguration( "", "" ) );
>    }
>
>    public void tearDown() 
>    {
>        m_manager = null;
>    }
>
>}
>
>
>------------------------------------------------------------------------
>
>--
>To unsubscribe, e-mail:   <mailto:avalon-dev-unsubscribe@jakarta.apache.org>
>For additional commands, e-mail: <mailto:avalon-dev-help@jakarta.apache.org>
>


Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message