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:[EMAIL PROTECTED]> >||| For additional commands, e-mail: <mailto:[EMAIL PROTECTED]> >||| >||| > > >------------------------------------------------------------------------ > >/* > * 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:[EMAIL PROTECTED]">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:[EMAIL PROTECTED]> >For additional commands, e-mail: <mailto:[EMAIL PROTECTED]> >