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>