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]>