bloritsch 2003/03/07 11:34:32
Added: component/src/java/org/apache/avalon/excalibur/testcase
BufferedLogger.java
CascadingAssertionFailedError.java
ComponentStateValidator.java ExcaliburTestCase.java
FullLifecycleComponent.java LatchedThreadGroup.java
Log:
merge testcase into component
Revision Changes Path
1.1
avalon-excalibur/component/src/java/org/apache/avalon/excalibur/testcase/BufferedLogger.java
Index: BufferedLogger.java
===================================================================
/*
* 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.testcase;
import org.apache.avalon.framework.ExceptionUtil;
import org.apache.avalon.framework.logger.Logger;
/**
* Simple Logger which logs all information to an internal StringBuffer.
* When logging is complete call toString() on the logger to obtain the
* logged output. Useful for testing.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2003/03/07 19:34:31 $
* @since 4.0
*/
public class BufferedLogger
implements Logger
{
private final StringBuffer m_sb = new StringBuffer();
/**
* Log a debug message.
*
* @param message the message
*/
public void debug( final String message )
{
debug( message, null );
}
/**
* Log a debug message.
*
* @param message the message
* @param throwable the throwable
*/
public void debug( final String message, final Throwable throwable )
{
append( "DEBUG", message, throwable );
}
/**
* Determine if messages of priority "debug" will be logged.
*
* @return true if "debug" messages will be logged
*/
public boolean isDebugEnabled()
{
return true;
}
/**
* Log a info message.
*
* @param message the message
*/
public void info( final String message )
{
info( message, null );
}
/**
* Log a info message.
*
* @param message the message
* @param throwable the throwable
*/
public void info( final String message, final Throwable throwable )
{
append( "INFO", message, throwable );
}
/**
* Determine if messages of priority "info" will be logged.
*
* @return true if "info" messages will be logged
*/
public boolean isInfoEnabled()
{
return true;
}
/**
* Log a warn message.
*
* @param message the message
*/
public void warn( final String message )
{
warn( message, null );
}
/**
* Log a warn message.
*
* @param message the message
* @param throwable the throwable
*/
public void warn( final String message, final Throwable throwable )
{
append( "WARN", message, throwable );
}
/**
* Determine if messages of priority "warn" will be logged.
*
* @return true if "warn" messages will be logged
*/
public boolean isWarnEnabled()
{
return true;
}
/**
* Log a error message.
*
* @param message the message
*/
public void error( final String message )
{
error( message, null );
}
/**
* Log a error message.
*
* @param message the message
* @param throwable the throwable
*/
public void error( final String message, final Throwable throwable )
{
append( "ERROR", message, throwable );
}
/**
* Determine if messages of priority "error" will be logged.
*
* @return true if "error" messages will be logged
*/
public boolean isErrorEnabled()
{
return true;
}
/**
* Log a fatalError message.
*
* @param message the message
*/
public void fatalError( final String message )
{
fatalError( message, null );
}
/**
* Log a fatalError message.
*
* @param message the message
* @param throwable the throwable
*/
public void fatalError( final String message, final Throwable throwable )
{
append( "FATAL ERROR", message, throwable );
}
/**
* Determine if messages of priority "fatalError" will be logged.
*
* @return true if "fatalError" messages will be logged
*/
public boolean isFatalErrorEnabled()
{
return true;
}
/**
* Create a new child logger.
* The name of the child logger is [current-loggers-name].[passed-in-name]
*
* @param name the subname of this logger
* @return the new logger
*/
public Logger getChildLogger( final String name )
{
return this;
}
/**
* Returns the contents of the buffer.
*
* @return the buffer contents
*
*/
public String toString()
{
return m_sb.toString();
}
private void append( final String level,
final String message,
final Throwable throwable )
{
synchronized( m_sb )
{
m_sb.append( level );
m_sb.append( " - " );
m_sb.append( message );
if( null != throwable )
{
final String stackTrace =
ExceptionUtil.printStackTrace( throwable );
m_sb.append( " : " );
m_sb.append( stackTrace );
}
m_sb.append( "\n" );
}
}
}
1.1
avalon-excalibur/component/src/java/org/apache/avalon/excalibur/testcase/CascadingAssertionFailedError.java
Index: CascadingAssertionFailedError.java
===================================================================
/*
* 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.testcase;
import junit.framework.AssertionFailedError;
import org.apache.avalon.framework.CascadingThrowable;
/**
* This is an extention to the testing framework so that we can get detailed
* messages from JUnit (The AssertionFailedError hides the underlying cause)
*
* @author <a href="mailto:[EMAIL PROTECTED]">Giacomo Pati</a>
* @version $Id: CascadingAssertionFailedError.java,v 1.1 2003/03/07 19:34:31
bloritsch Exp $
*/
public class CascadingAssertionFailedError
extends AssertionFailedError
implements CascadingThrowable
{
private final Throwable m_throwable;
/**
* Constructor with no message
*/
public CascadingAssertionFailedError()
{
this( null, null );
}
/**
* Constructor with a message
*/
public CascadingAssertionFailedError( String message )
{
this( message, null );
}
/**
* Constructor with a message and a parent exception
*/
public CascadingAssertionFailedError( String message,
Throwable parentThrowable )
{
super( message );
m_throwable = parentThrowable;
}
/**
* Return the parent exception
*/
public final Throwable getCause()
{
return m_throwable;
}
}
1.1
avalon-excalibur/component/src/java/org/apache/avalon/excalibur/testcase/ComponentStateValidator.java
Index: ComponentStateValidator.java
===================================================================
/*
* 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.testcase;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Suspendable;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.Recomposable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Reconfigurable;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.Recontextualizable;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Loggable;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.service.Serviceable;
/**
* This class provides basic facilities for enforcing Avalon's contracts
* within your own code.
*
* Based on Avalon version from Sandbox.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Berin Loritsch</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Michael McKibben</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Leif Mortenson</a>
* @version CVS $Revision: 1.1 $ $Date: 2003/03/07 19:34:31 $
*/
public final class ComponentStateValidator
{
private static final String WRITE_FAIL = "Value is already bound";
// Interfaces
private static final long LOG_ENABLED = 0x00000001;
private static final long LOGGABLE = 0x00000002;
private static final long CONTEXTUALIZABLE = 0x00000004;
private static final long COMPOSABLE = 0x00000008;
private static final long SERVICEABLE = 0x00000010;
private static final long CONFIGURABLE = 0x00000020;
private static final long PARAMETERIZABLE = 0x00000040;
private static final long INITIALIZABLE = 0x00000080;
private static final long STARTABLE = 0x00000100;
private static final long SUSPENDABLE = 0x00001000;
private static final long RECONTEXTUALIZABLE = 0x00002000;
private static final long RECOMPOSABLE = 0x00004000;
private static final long RECONFIGURABLE = 0x00008000;
private static final long DISPOSABLE = 0x00100000;
// Initialization Methods.
private static final long ENABLE_LOGGING = 0x00000001;
private static final long SET_LOGGER = 0x00000002;
private static final long CONTEXTUALIZE = 0x00000004;
private static final long SERVICE = 0x00000008;
private static final long COMPOSE = 0x00000010;
private static final long CONFIGURE = 0x00000020;
private static final long PARAMETERIZE = 0x00000040;
private static final long INITIALIZE = 0x00000080;
private static final long START = 0x00000100;
private static final long INIT_COMPLETE = 0x00000400;
// Active Service Methods
private static final long SUSPEND = 0x00001000;
private static final long RECONTEXTUALIZE = 0x00002000;
private static final long RECOMPOSE = 0x00004000;
private static final long RECONFIGURE = 0x00008000;
private static final long RESUME = 0x00010000;
// Destruction Methods
private static final long STOP = 0x00100000;
private static final long DISPOSE = 0x00200000;
// Masks
private static final long INIT_MASK = ENABLE_LOGGING | SET_LOGGER |
CONTEXTUALIZE | COMPOSE | SERVICE | CONFIGURE | PARAMETERIZE | INITIALIZE |
START;
private final long m_interfaces;
private final long m_methods;
private long m_state;
private boolean m_active;
private final Object m_object;
/**
* Create state validator from object (this can be used for more than just
* components).
*/
public ComponentStateValidator( final Object object )
{
m_object = object;
long methods = 0;
long interfaces = 0;
if( object instanceof LogEnabled )
{
interfaces |= LOG_ENABLED;
methods |= ENABLE_LOGGING;
}
if( object instanceof Loggable )
{
interfaces |= LOGGABLE;
methods |= SET_LOGGER;
}
if( object instanceof Contextualizable )
{
interfaces |= CONTEXTUALIZABLE;
methods |= CONTEXTUALIZE;
}
if( object instanceof Serviceable )
{
interfaces |= SERVICEABLE;
methods |= SERVICE;
}
if( object instanceof Composable )
{
if( ( interfaces & SERVICEABLE ) > 0 )
{
throw new IllegalStateException( "Cannot implement Composable and
Serviceable together" );
}
interfaces |= COMPOSABLE;
methods |= COMPOSE;
}
if( object instanceof Configurable )
{
interfaces |= CONFIGURABLE;
methods |= CONFIGURE;
}
if( object instanceof Parameterizable )
{
interfaces |= PARAMETERIZABLE;
methods |= PARAMETERIZE;
}
if( object instanceof Initializable )
{
interfaces |= INITIALIZABLE;
methods |= INITIALIZE;
}
if( object instanceof Startable )
{
interfaces |= STARTABLE;
methods |= START | STOP;
}
if( object instanceof Suspendable )
{
interfaces |= SUSPENDABLE;
methods |= SUSPEND | RESUME;
}
if( object instanceof Recontextualizable )
{
interfaces |= RECONTEXTUALIZABLE;
methods |= RECONTEXTUALIZE;
}
if( object instanceof Recomposable )
{
interfaces |= RECOMPOSABLE;
methods |= RECOMPOSE;
}
if( object instanceof Reconfigurable )
{
interfaces |= RECONFIGURABLE;
methods |= RECONFIGURE;
}
if( object instanceof Disposable )
{
interfaces |= DISPOSABLE;
methods |= DISPOSE;
}
m_methods = methods;
m_interfaces = interfaces;
generalCheckInitComplete();
}
private String getInterfaceName( long interfaceId )
{
if( interfaceId == LOG_ENABLED )
{
return LogEnabled.class.getName();
}
else if( interfaceId == LOGGABLE )
{
return Loggable.class.getName();
}
else if( interfaceId == CONTEXTUALIZABLE )
{
return Contextualizable.class.getName();
}
else if( interfaceId == SERVICEABLE )
{
return Serviceable.class.getName();
}
else if( interfaceId == COMPOSABLE )
{
return Composable.class.getName();
}
else if( interfaceId == CONFIGURABLE )
{
return Configurable.class.getName();
}
else if( interfaceId == PARAMETERIZABLE )
{
return Parameterizable.class.getName();
}
else if( interfaceId == INITIALIZABLE )
{
return Initializable.class.getName();
}
else if( interfaceId == STARTABLE )
{
return Startable.class.getName();
}
else if( interfaceId == SUSPENDABLE )
{
return Suspendable.class.getName();
}
else if( interfaceId == RECONTEXTUALIZABLE )
{
return Recontextualizable.class.getName();
}
else if( interfaceId == RECOMPOSABLE )
{
return Recomposable.class.getName();
}
else if( interfaceId == RECONFIGURABLE )
{
return Reconfigurable.class.getName();
}
else if( interfaceId == DISPOSABLE )
{
return Disposable.class.getName();
}
else
{
throw new IllegalStateException( "Unknown Interface Id " + interfaceId );
}
}
private String getMethodName( long methodId )
{
if( methodId == ENABLE_LOGGING )
{
return "enableLogging()";
}
else if( methodId == SET_LOGGER )
{
return "setLogger()";
}
else if( methodId == CONTEXTUALIZE )
{
return "contextualize()";
}
else if( methodId == SERVICE )
{
return "service()";
}
else if( methodId == COMPOSE )
{
return "compose()";
}
else if( methodId == CONFIGURE )
{
return "configure()";
}
else if( methodId == PARAMETERIZE )
{
return "parameterize()";
}
else if( methodId == INITIALIZE )
{
return "initialize()";
}
else if( methodId == START )
{
return "start()";
}
else if( methodId == SUSPEND )
{
return "suspend()";
}
else if( methodId == RECONTEXTUALIZE )
{
return "recontextualize()";
}
else if( methodId == RECOMPOSE )
{
return "recompose()";
}
else if( methodId == RECONFIGURE )
{
return "reconfigure()";
}
else if( methodId == RESUME )
{
return "resume()";
}
else if( methodId == STOP )
{
return "stop()";
}
else if( methodId == DISPOSE )
{
return "dispose()";
}
else
{
throw new IllegalStateException( "Unknown Method Id " + methodId );
}
}
private String getLastMethod( long state )
{
for( int i = 31; i >= 0; i-- )
{
long methodId = 0x1 << i;
if( ( state & methodId ) != 0 )
{
return getMethodName( methodId );
}
}
throw new IllegalStateException( "No last state method found for state " +
state );
}
/**
* Test to see if this was the last initialization method.
*/
private void generalCheckInitComplete()
{
if( m_state == ( m_methods & INIT_MASK ) )
{
// All init methods called
m_active = true;
}
}
/**
* Initialization methods must be called in order, must all be called, may
* not be called more than once, and may not be called once any of the
* Descruction methods have been called.
*/
private void generalCheckInit( final String message, final long interfaceId,
final long methodId )
{
if( ( m_interfaces & interfaceId ) == 0 )
{
// Interface not implemented
if( message == null )
{
throw new IllegalStateException( m_object.getClass().getName() +
" does not implement " +
getInterfaceName( interfaceId ) + "." );
}
else
{
throw new IllegalStateException( message );
}
}
else if( ( m_state & methodId ) > 0 )
{
// Method already called.
if( message == null )
{
throw new IllegalStateException( getMethodName( methodId ) + "
already called." );
}
else
{
throw new IllegalStateException( message );
}
}
else if( m_state > methodId )
{
// Method called after a descruction method was called.
if( message == null )
{
throw new IllegalStateException( getMethodName( methodId ) +
" can not be called after " +
getLastMethod( m_state ) + "." );
}
else
{
throw new IllegalStateException( message );
}
}
else if( ( m_state & ( methodId - 1 ) ) != ( m_methods & ( methodId - 1 ) ) )
{
// One or more of the methods that should have been called before
// this method was not.
if( message == null )
{
throw new IllegalStateException( getMethodName( methodId ) +
" called out of order. " +
getLastMethod( m_methods & ( methodId - 1 ) ) +
" must be called first." );
}
else
{
throw new IllegalStateException( message );
}
}
// Add this method to the state
m_state |= methodId;
// See if the initialization is complete.
generalCheckInitComplete();
}
/**
* Active Service methods may only be called after all of the
* Initialization methods have been called, any before any of the
* Descruction methods have been called. While in the active state,
* the contracts of the methods allow the active state methods to be
* called any number of times, in any order.
* The resume() method should do nothing if suspend() has not yet been
* called for example.
*/
private void generalCheckActive( final String message, final long interfaceId,
final long methodId )
{
if( ( m_interfaces & interfaceId ) == 0 )
{
// Interface not implemented
if( message == null )
{
throw new IllegalStateException( m_object.getClass().getName() +
" does not implement " +
getInterfaceName( interfaceId ) + "." );
}
else
{
throw new IllegalStateException( message );
}
}
else if( !m_active )
{
// Component not in the active state.
if( m_state < INIT_COMPLETE )
{
// Still expecting initialization methods.
if( message == null )
{
throw new IllegalStateException( getMethodName( methodId ) +
" called before component was
made active. " +
getLastMethod( m_methods & (
INIT_COMPLETE - 1 ) ) +
" must be called first." );
}
else
{
throw new IllegalStateException( message );
}
}
else
{
// One or more destruction methods have been called.
if( message == null )
{
throw new IllegalStateException( getMethodName( methodId ) +
" called after component was
made inactive. Cannot call after " +
getLastMethod( m_state ) + "."
);
}
else
{
throw new IllegalStateException( message );
}
}
}
}
/**
* Descruction Methods must be called in order. They may be called before
* all of the Initialization methods have been called if there was an
* error.
*/
private void generalCheckDest( final String message, final long interfaceId,
final long methodId )
{
if( ( m_interfaces & interfaceId ) == 0 )
{
// Interface not implemented
if( message == null )
{
throw new IllegalStateException( m_object.getClass().getName() +
" does not implement " +
getInterfaceName( interfaceId ) + "." );
}
else
{
throw new IllegalStateException( message );
}
}
else if( m_state > methodId )
{
// Method called after a later descruction method was called.
if( message == null )
{
throw new IllegalStateException( getMethodName( methodId ) +
" can not be called after " +
getLastMethod( m_state ) + "." );
}
else
{
throw new IllegalStateException( message );
}
}
// Add this method to the state
m_state |= methodId;
// Deactivate
m_active = false;
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the ENABLE_LOGGING state has already been set, if the component implements
* LogEnabled, and if the state has progressed beyond the Logger stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkLogEnabled()
{
checkLogEnabled( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the ENABLE_LOGGING state has already been set, if the component implements
* LogEnabled, and if the state has progressed beyond the Logger stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkLogEnabled( final String message )
{
generalCheckInit( message, LOG_ENABLED, ENABLE_LOGGING );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SET_LOGGER state has already been set, if the component implements
* Loggable, and if the state has progressed beyond the Logger stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkLoggable()
{
checkLogEnabled( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SET_LOGGER state has already been set, if the component implements
* Loggable, and if the state has progressed beyond the Logger stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkLoggable( final String message )
{
generalCheckInit( message, LOGGABLE, SET_LOGGER );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the CONTEXTUALIZED state has already been set, if the component implements
* Contextualizable, and if the state has progressed beyond the Context stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkContextualized()
{
checkContextualized( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the CONTEXTUALIZED state has already been set, if the component implements
* Contextualizable, and if the state has progressed beyond the Context stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkContextualized( final String message )
{
generalCheckInit( message, CONTEXTUALIZABLE, CONTEXTUALIZE );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SERVICE state has already been set, if the component implements
* Composable, and if the state has progressed beyond the Configuration stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkServiced()
{
checkServiced( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SERVICE state has already been set, if the component implements
* Composable, and if the state has progressed beyond the Configuration stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkServiced( final String message )
{
generalCheckInit( message, SERVICEABLE, SERVICE );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the COMPOSED state has already been set, if the component implements
* Composable, and if the state has progressed beyond the Configuration stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkComposed()
{
checkComposed( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the COMPOSED state has already been set, if the component implements
* Composable, and if the state has progressed beyond the Configuration stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkComposed( final String message )
{
generalCheckInit( message, COMPOSABLE, COMPOSE );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the CONFIGURED state has already been set, if the component implements
* Configurable, and if the state has progressed beyond the Configuration stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkConfigured()
{
checkConfigured( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the CONFIGURED state has already been set, if the component implements
* Configurable, and if the state has progressed beyond the Configuration stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkConfigured( final String message )
{
generalCheckInit( message, CONFIGURABLE, CONFIGURE );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the PARAMETERIZED state has already been set, if the component implements
* Parameterizable, and if the state has progressed beyond the Parameters stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkParameterized()
{
checkParameterized( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the PARAMETERIZED state has already been set, if the component implements
* Parameterizable, and if the state has progressed beyond the Parameters stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkParameterized( final String message )
{
generalCheckInit( message, PARAMETERIZABLE, PARAMETERIZE );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the INITIALIZED state has already been set, if the component implements
* Initializable, and if the state has progressed beyond the
<code>initialize</code> stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkInitialized()
{
checkInitialized( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the INITIALIZED state has already been set, if the component implements
* Initializable, and if the state has progressed beyond the
<code>initialize</code> stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkInitialized( final String message )
{
generalCheckInit( message, INITIALIZABLE, INITIALIZE );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the STARTED state has already been set, if the component implements
* Startable, and if the state has progressed beyond the <code>start</code>
stage.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkStarted()
{
checkStarted( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the STARTED state has already been set, if the component implements
* Startable, and if the state has progressed beyond the <code>start</code>
stage.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkStarted( final String message )
{
generalCheckInit( message, STARTABLE, START );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SUSPENDED state has already been set, if the component implements
* Suspendable, and if the Component is active.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkSuspended()
{
checkSuspended( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SUSPENDED state has already been set, if the component implements
* Suspendable, and if the Component is active.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkSuspended( final String message )
{
generalCheckActive( message, SUSPENDABLE, SUSPEND );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SUSPENDED state has not been set, if the component implements
* Suspendable, and if the Component is active.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkResumed()
{
checkResumed( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the SUSPENDED state has not been set, if the component implements
* Suspendable, and if the Component is active.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkResumed( final String message )
{
generalCheckActive( message, SUSPENDABLE, RESUME );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the STOPPED state has not been set, if the component implements
* Startable, and if the Component is active.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkStopped()
{
checkStopped( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the STOPPED state has not been set, if the component implements
* Startable, and if the Component is active.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkStopped( final String message )
{
generalCheckDest( message, STARTABLE, STOP );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the DISPOSED state has not been set, if the component implements
* Disposable.
*
* @throws IllegalStateException if the state is manage out of order
*/
public void checkDisposed()
{
checkDisposed( null );
}
/**
* Throw an exception if the initialization is out of order. It tests to see
* if the DISPOSED state has not been set, if the component implements
* Disposable.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkDisposed( final String message )
{
generalCheckDest( message, DISPOSABLE, DISPOSE );
}
/**
* Checks to see if the state is active.
*
* @throws IllegalStateException if the component is not active
*/
public void checkActive()
{
checkActive( null );
}
/**
* Checks to see if the state is active.
*
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the component is not active
*/
public void checkActive( final String message )
{
if( isActive() )
{
return;
}
// Component not in the active state.
if( m_state < INIT_COMPLETE )
{
// Still expecting initialization methods.
if( message == null )
{
throw new IllegalStateException( "Component not in the active state.
" +
getLastMethod( m_methods & (
INIT_COMPLETE - 1 ) ) +
" was not called." );
}
else
{
throw new IllegalStateException( message );
}
}
else
{
// One or more destruction methods have been called.
if( message == null )
{
throw new IllegalStateException( "Component not in the active state
because " +
getLastMethod( m_state ) + " was
called." );
}
else
{
throw new IllegalStateException( message );
}
}
}
/**
* Checks to see if the state is active, and returns true or false.
*
* @return <code>true</code> if active, <code>false</code> if not
*/
public boolean isActive()
{
return m_active;
}
/**
* Make sure object has not been assigned yet.
*
* @param object to test
* @throws IllegalStateException if the state is manage out of order
*/
public void checkNotAssigned( final Object object )
{
checkNotAssigned( object, WRITE_FAIL );
}
/**
* Make sure object has not been assigned yet.
*
* @param object to test
* @param message the message to include in the thrown exception
* @throws IllegalStateException if the state is manage out of order
*/
public void checkNotAssigned( final Object object, final String message )
{
if( null != object )
{
throw new IllegalStateException( message );
}
}
}
1.1
avalon-excalibur/component/src/java/org/apache/avalon/excalibur/testcase/ExcaliburTestCase.java
Index: ExcaliburTestCase.java
===================================================================
/*
* 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.testcase;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import junit.framework.TestResult;
import org.apache.avalon.excalibur.component.DefaultRoleManager;
import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
import org.apache.avalon.excalibur.logger.DefaultLogKitManager;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.logger.LogKitLogger;
import org.apache.log.Hierarchy;
import org.apache.log.LogTarget;
import org.apache.log.Logger;
import org.apache.log.Priority;
import org.apache.log.format.PatternFormatter;
import org.apache.log.output.io.StreamTarget;
/**
* JUnit TestCase for Avalon Components.
* <p>
* This class extends the JUnit TestCase class to setup an environment which
* makes it possible to easily test Avalon Components. The following methods
* and instance variables are exposed for convenience testing:
* </p>
* <dl>
* <dt>manager</dt>
* <dd>
* This instance variable contains an initialized ComponentLocator which
* can be used to lookup Components configured in the test configuration
* file. (see below)
* </dd>
* <dt>getLogger()</dt>
* <dd>
* This method returns the default logger for this test case
* </dd>
* </dl>
* <p>
* The following test case configuration can be used as a basis for new tests.
* Detailed are explanations of the configuration elements can be found after
* the example. The example will log all logger output to the console and to
* a log file.
* </p>
* <pre>
* <testcase>
* <annotation>
* <![CDATA[
* <title>{Name of test}</title>
* <para>
* {Description of test}
* The configuration is specified in the file located in
* <parameter>avalon-excalibur/src/test/{path and name of conf
file}.xtext</parameter>.
* </para>
* ]]>
* </annotation>
*
* <logkit log-level="INFO">
* <factories>
* <factory type="stream"
class="org.apache.avalon.excalibur.logger.factory.StreamTargetFactory"/>
* <factory type="file"
class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/>
* </factories>
*
* <targets>
* <stream id="console">
* <stream>System.out</stream>
* <format type="avalon">
* %7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS}
[%30.30{category}] (%{context}): %{message}\n%{throwable}
* </format>
* </stream>
* <file id="log-file">
* <filename>TEST-{full test class name}.log</filename>
* <format type="avalon">
* %7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS}
[%30.30{category}] (%{context}): %{message}\n%{throwable}
* </format>
* </file>
* </targets>
*
* <categories>
* <category name="test" log-level="INFO">
* <log-target id-ref="console"/>
* <log-target id-ref="log-file"/>
* </category>
* <category name="jdbc" log-level="INFO">
* <log-target id-ref="console"/>
* <log-target id-ref="log-file"/>
* </category>
* </categories>
* </logkit>
*
* <context>
* <entry name="foo" value="bar"/>
* <entry name="baz" class="my.context.Class"/>
* </context>
*
* <roles>
* <role
name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
* shorthand="datasources"
*
default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
* <hint shorthand="jdbc"
class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
* </role>
* </roles>
*
* <components>
* <datasources>
* <jdbc name="personell" logger="jdbc">
* <pool-controller min="5" max="10"/>
* <jdbc name="personnel"/>
* <dburl>jdbc:odbc:test</dburl>
* <user>test</user>
* <password>test</password>
* <driver>sun.jdbc.odbc.JdbcOdbcDriver</driver>
* </jdbc>
* </datasources>
* </components>
* </testcase>
* </pre>
* <p>
* Element Explanation:
* <dl>
* <dt>testcase</dt>
* <dd>Defines a test case configuration. Must contain one each of the
* following elements: <code>annotation</code>, <code>logkit</code>,
* <code>context</code>, <code>roles</code>, and <code>components</code>
* </dd>.
*
* <dt>annotation</dt>
* <dd>Defines a test annotation. This element should define a block of
* XML enclosed within a CDATA element. The XML should be made up of a
* <code>title</code> element, naming the test, and a <code>para</code>
* element which is used to describe the test.</dd>
*
* <dt>logkit</dt>
* <dd>Configures the logger used by the test cases and the components used
* by the tests. The <code>logkit</code> element takes two optional
* attributes:
* <dl>
* <dt>logger</dt><dd>Uses to name the logger which is used to bootstrap
* the LogKit logger. (Defaults to <code>"lm"</code>)</dd>
* <dt>log-level</dt><dd>Because the logger used by the LogKit must be
* created before the Log Kit Manager is initialized, it must be fully
* configured before the <code>logkit</code> element is parsed. This
* attribute allows the Log Kit's log priority to be set. This log
* level will also become the default for the Role Manager, Component
* Manager, and all components if they do not have <code>category</code>
* elements declated in the <code>logkit</code> element.
* (Defaults to "INFO")</dd>
* </dl>
* The loggers used by test cases and components can be easily configured
* from within this file. The default test configuration, shown above,
* includes a "test" category. This category is used to configure the
* default logger for all test cases. If it is set to "DEBUG", then all
* test debug logging will be enabled. To enalble debug logging for a
* single test case, a child category must be defined for the
* "testCheckTotals" test case as follows:
* <pre>
* <categories>
* <category name="test" log-level="INFO">
* <log-target id-ref="console"/>
* <log-target id-ref="log-file"/>
*
* <category name="testCheckTotals" log-level="DEBUG">
* <log-target id-ref="console"/>
* <log-target id-ref="log-file"/>
* </category>
* </category>
* </categories>
* </pre>
* For general information on how to configure the LogKit Manager, please
* refer to the Log Kit documentation.
* </dd>
*
* <dt>context</dt>
* <dd>Allows context properties to be set in the context passed to any
* Contextualizable components.</dd>
*
* <dt>roles</dt>
* <dd>Roles configuration for the Components configured in the
* <code>components</code> element. The logger used by the RoleManager
* can be configured using a <code>logger</code> attribute, which defaults
* to "rm". By default this logger will have the same log level and
* formatting as the LogKit logger. It can be configured by adding a
* <code>category</code> within the <code>logkit</code> element.</dd>
*
* <dt>components</dt>
* <dd>Used to configure any Components used by the test cases. The logger
* used by the ComponentLocator can be configured using a <code>logger</code>
* attribute, which defaults to "cm". By default this logger will have the
* same log level and formatting as the LogKit logger. It can be configured
* by adding a <code>category</code> within the <code>logkit</code> element.
* </dd>
*
* </dl>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Giacomo Pati</a>
* @version $Id: ExcaliburTestCase.java,v 1.1 2003/03/07 19:34:31 bloritsch Exp $
*/
public class ExcaliburTestCase
extends TestCase
{
///Format of default formatter
private static final String FORMAT =
"%7.7{priority} %23.23{time:yyyy-MM-dd' 'HH:mm:ss.SSS} [%30.30{category}]
(%{context}): %{message}\n%{throwable}";
//The default logger
private Logger m_logger;
private LogKitLogger m_logEnabledLogger;
private ExcaliburComponentManager m_manager;
private DefaultLogKitManager m_logKitManager;
private static HashMap m_tests = new HashMap();
protected ComponentManager manager;
public ExcaliburTestCase( final String name )
{
super( name );
ArrayList methodList = (ArrayList)ExcaliburTestCase.m_tests.get( getClass()
);
Method[] methods = getClass().getMethods();
if( null == methodList )
{
methodList = new ArrayList( methods.length );
for( int i = 0; i < methods.length; i++ )
{
String methodName = methods[ i ].getName();
if( methodName.startsWith( "test" ) &&
( Modifier.isPublic( methods[ i ].getModifiers() ) ) &&
( methods[ i ].getReturnType().equals( Void.TYPE ) ) &&
( methods[ i ].getParameterTypes().length == 0 ) )
{
methodList.add( methodName );
}
}
ExcaliburTestCase.m_tests.put( getClass(), methodList );
}
}
/** Return the logger */
protected Logger getLogger()
{
return m_logger;
}
/** Return the logger */
protected LogKitLogger getLogEnabledLogger()
{
return m_logEnabledLogger;
}
/**
* Initializes the ComponentLocator
*
* The configuration file is determined by the class name plus .xtest appended,
* all '.' replaced by '/' and loaded as a resource via classpath
*/
protected void prepare()
throws Exception
{
final String resourceName = getClass().getName().replace( '.', '/' ) +
".xtest";
URL resource = getClass().getClassLoader().getResource( resourceName );
if( resource != null )
{
getLogger().debug( "Loading resource " + resourceName );
prepare( resource.openStream() );
}
else
{
getLogger().debug( "Resource not found " + resourceName );
}
}
/**
* Initializes the ComponentLocator
*
* @param testconf The configuration file is passed as a <code>InputStream</code>
*
* A common way to supply a InputStream is to overwrite the initialize() method
* in the sub class, do there whatever is needed to get the right InputStream
object
* supplying a conformant xtest configuartion and pass it to this initialize
method.
* the mentioned initialize method is also the place to set a different logging
priority
* to the member variable m_logPriority.
*/
protected final void prepare( final InputStream testconf )
throws Exception
{
getLogger().debug( "ExcaliburTestCase.initialize" );
final DefaultConfigurationBuilder builder = new
DefaultConfigurationBuilder();
final Configuration conf = builder.build( testconf );
String annotation = conf.getChild( "annotation" ).getValue( null );
if( ( null != annotation ) && !( "".equals( annotation ) ) )
{
m_logger.info( annotation );
}
Context context = setupContext( conf.getChild( "context" ) );
setupManagers( conf.getChild( "components" ),
conf.getChild( "roles" ),
conf.getChild( "logkit" ),
context );
manager = m_manager;
setCurrentLogger( "prepare" );
}
/**
* Disposes the <code>ComponentLocator</code>
*/
final private void done()
{
if( null != m_manager )
{
m_manager.dispose();
}
m_manager = null;
}
/**
* Exctract the base class name of a class.
*/
private String getBaseClassName( Class clazz )
{
String name = clazz.getName();
int pos = name.lastIndexOf( '.' );
if( pos >= 0 )
{
name = name.substring( pos + 1 );
}
return name;
}
/**
* Override <code>run</code> so that we can have code that is run once.
*/
final public void run( TestResult result )
{
ArrayList methodList = (ArrayList)ExcaliburTestCase.m_tests.get( getClass()
);
if( null == methodList || methodList.isEmpty() )
{
return; // The test was already run! NOTE: this is a hack.
}
// Set the logger for the initialization phase.
setCurrentLogger( getBaseClassName( getClass() ) );
try
{
if( this instanceof Initializable )
{
( (Initializable)this ).initialize();
}
prepare();
Iterator tests = methodList.iterator();
while( tests.hasNext() )
{
String methodName = (String)tests.next();
setName( methodName );
setCurrentLogger( methodName );
if( getLogger().isDebugEnabled() )
{
getLogger().debug( "" );
getLogger().debug( "========================================" );
getLogger().debug( " begin test: " + methodName );
getLogger().debug( "========================================" );
}
super.run( result );
if( getLogger().isDebugEnabled() )
{
getLogger().debug( "========================================" );
getLogger().debug( " end test: " + methodName );
getLogger().debug( "========================================" );
getLogger().debug( "" );
}
}
}
catch( Exception e )
{
System.out.println( e );
e.printStackTrace();
result.addError( this, e );
}
finally
{
done();
if( this instanceof Disposable )
{
try
{
( (Disposable)this ).dispose();
}
catch( Exception e )
{
result.addFailure( this, new AssertionFailedError( "Disposal
Error" ) );
}
}
}
methodList.clear();
ExcaliburTestCase.m_tests.put( getClass(), methodList );
}
/**
* Sets the logger which will be returned by getLogger and getLogEnabledLogger
*/
final private void setCurrentLogger( String name )
{
org.apache.log.Logger logger;
if( m_logKitManager == null )
{
// Logger for the portion of the configuration has been loaded.
logger = Hierarchy.getDefaultHierarchy().getLoggerFor( name );
logger.setPriority( Priority.INFO );
PatternFormatter formatter = new PatternFormatter( FORMAT );
StreamTarget target = new StreamTarget( System.out, formatter );
logger.setLogTargets( new LogTarget[]{target} );
}
else
{
logger = m_logKitManager.getLogger( "test." + name );
}
/*
//FIXME(GP): This method should setup a LogConfigurator and LogManager
// according to the configuration spec. not yet
completed/implemented
// It will return a default logger for now.
final org.apache.log.Logger logger =
Hierarchy.getDefaultHierarchy().getLoggerFor( name );
logger.setPriority( m_logPriority );
final PatternFormatter formatter = new PatternFormatter( FORMAT );
final StreamTarget target = new StreamTarget( System.out, formatter );
logger.setLogTargets( new LogTarget[] { target } );
*/
m_logger = logger;
m_logEnabledLogger = new LogKitLogger( m_logger );
}
/**
* set up a context according to the xtest configuration specifications context
* element.
*
* A method addContext(DefaultContext context) is called here to enable
subclasses
* to put additional objects into the context programmatically.
*/
final private Context setupContext( final Configuration conf )
throws Exception
{
//FIXME(GP): This method should setup the Context object according to the
// configuration spec. not yet completed
final DefaultContext context = new DefaultContext();
final Configuration[] confs = conf.getChildren( "entry" );
for( int i = 0; i < confs.length; i++ )
{
final String key = confs[ i ].getAttribute( "name" );
final String value = confs[ i ].getAttribute( "value", null );
if( value == null )
{
String clazz = confs[ i ].getAttribute( "class" );
Object obj = getClass().getClassLoader().loadClass( clazz
).newInstance();
context.put( key, obj );
if( getLogger().isInfoEnabled() )
getLogger().info( "ExcaliburTestCase: added an instance of class
" + clazz + " to context entry " + key );
}
else
{
context.put( key, value );
if( getLogger().isInfoEnabled() )
getLogger().info( "ExcaliburTestCase: added value \"" + value +
"\" to context entry " + key );
}
}
addContext( context );
return ( context );
}
/**
* This method may be overwritten by subclasses to put additional objects
* into the context programmatically.
*/
protected void addContext( DefaultContext context )
{
}
final private void setupManagers( final Configuration confCM,
final Configuration confRM,
final Configuration confLM,
final Context context )
throws Exception
{
// Setup the log manager. Get the logger name and log level from attributes
// in the <logkit> node
String lmLoggerName = confLM.getAttribute( "logger", "lm" );
String lmLogLevel = confLM.getAttribute( "log-level", "INFO" );
Priority lmPriority = Priority.getPriorityForName( lmLogLevel );
DefaultLogKitManager logKitManager = new DefaultLogKitManager();
Logger lmLogger = Hierarchy.getDefaultHierarchy().getLoggerFor( lmLoggerName
);
lmLogger.setPriority( lmPriority );
logKitManager.enableLogging( new LogKitLogger( lmLogger ) );
logKitManager.contextualize( context );
logKitManager.configure( confLM );
Hierarchy h = logKitManager.getHierarchy();
h.setDefaultPriority( lmPriority );
m_logKitManager = logKitManager;
// Setup the RoleManager
String rmLoggerName = confRM.getAttribute( "logger", "rm" );
DefaultRoleManager roleManager = new DefaultRoleManager();
roleManager.setLogger( logKitManager.getLogger( rmLoggerName ) );
roleManager.configure( confRM );
// Set up the ComponentLocator
String cmLoggerName = confCM.getAttribute( "logger", "cm" );
ExcaliburComponentManager manager = new ExcaliburComponentManager();
manager.setLogger( logKitManager.getLogger( cmLoggerName ) );
manager.setLogKitManager( logKitManager );
manager.contextualize( context );
manager.setRoleManager( roleManager );
manager.configure( confCM );
manager.initialize();
m_manager = manager;
}
protected final Object lookup( final String key )
throws ComponentException
{
return manager.lookup( key );
}
protected final void release( final Component object )
{
manager.release( object );
}
}
1.1
avalon-excalibur/component/src/java/org/apache/avalon/excalibur/testcase/FullLifecycleComponent.java
Index: FullLifecycleComponent.java
===================================================================
/*
* 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.testcase;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.activity.Suspendable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* This test class is used to test the AbstractComponent facilities for you.
*
* @author <a href="[EMAIL PROTECTED]">Berin Loritsch</a>
* @version CVS $Revision: 1.1 $ $Date: 2003/03/07 19:34:31 $
*/
public final class FullLifecycleComponent
implements LogEnabled, Contextualizable, Parameterizable, Configurable,
Serviceable, Initializable, Startable, Suspendable, Disposable,
ThreadSafe
{
private ComponentStateValidator m_validator = new ComponentStateValidator( this
);
private Logger m_logger;
private Context m_context;
private Parameters m_parameters;
private Configuration m_configuration;
private ServiceManager m_manager;
public void enableLogging( Logger logger )
{
m_validator.checkNotAssigned( m_logger );
m_validator.checkLogEnabled();
m_logger = logger;
}
public void contextualize( Context context )
throws ContextException
{
m_validator.checkNotAssigned( m_context );
m_validator.checkContextualized();
m_context = context;
}
public void parameterize( Parameters params )
throws ParameterException
{
m_validator.checkNotAssigned( m_parameters );
m_validator.checkParameterized();
m_parameters = params;
}
public void configure( Configuration config )
throws ConfigurationException
{
m_validator.checkNotAssigned( m_configuration );
m_validator.checkConfigured();
m_configuration = config;
}
public void service( ServiceManager manager )
throws ServiceException
{
m_validator.checkNotAssigned( m_manager );
m_validator.checkComposed();
m_manager = manager;
}
public void initialize()
throws Exception
{
m_validator.checkInitialized();
}
public void start()
throws Exception
{
m_validator.checkStarted();
}
public void suspend()
{
m_validator.checkSuspended();
}
public void resume()
{
m_validator.checkResumed();
}
public void stop()
throws Exception
{
m_validator.checkStopped();
}
public void dispose()
{
m_validator.checkDisposed();
m_logger = null;
m_context = null;
m_parameters = null;
m_configuration = null;
m_manager = null;
}
}
1.1
avalon-excalibur/component/src/java/org/apache/avalon/excalibur/testcase/LatchedThreadGroup.java
Index: LatchedThreadGroup.java
===================================================================
/*
* 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.testcase;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
/**
* This class is useful for writing MultiThreaded test cases where you need to
perform
* multithreaded load testing on a component.
* <p>
* An instance of will create a block of threads of the specified size. Each thread
will be
* assigned to run a specified Runnable instance. The threads will then all wait
at a latch
* until the go method is called. The go method will not return until all of the
* Runnables have completed.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Leif Mortenson</a>
* @version $Id: LatchedThreadGroup.java,v 1.1 2003/03/07 19:34:31 bloritsch Exp $
*/
public class LatchedThreadGroup
extends AbstractLogEnabled
{
private Thread[] m_threads;
private Object m_semaphore = new Object();
private int m_startedCount;
private boolean m_latched;
private int m_completedCount;
private Throwable m_exception;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
/**
* Creates a LatchedThreadGroup with a thread for each Runnable in the runnables
array.
*/
public LatchedThreadGroup( Runnable[] runnables )
{
int threadCount = runnables.length;
m_threads = new Thread[ threadCount ];
for( int i = 0; i < threadCount; i++ )
{
m_threads[ i ] = new Runner( runnables[ i ], "Latched_Thread_" + i );
}
}
/**
* Creates a LatchedThreadGroup with threadCount threads each running runnable.
*/
public LatchedThreadGroup( Runnable runnable, int threadCount )
{
m_threads = new Thread[ threadCount ];
for( int i = 0; i < threadCount; i++ )
{
m_threads[ i ] = new Runner( runnable, "Latched_Thread_" + i );
}
}
/*---------------------------------------------------------------
* Methods
*-------------------------------------------------------------*/
protected void resetMemory()
{
System.gc();
System.gc();
// Let the system settle down.
try
{
Thread.sleep( 50 );
}
catch( InterruptedException e )
{
}
Runtime runtime = Runtime.getRuntime();
getLogger().debug( "Memory: " + ( runtime.totalMemory() -
runtime.freeMemory() ) );
}
/**
* Causes all of the Runnables to start at the same instance. This method will
return
* once all of the Runnables have completed.
*
* @return time, in milliseconds, that it took for all of the Runnables to
complete.
*/
public long go()
throws Exception
{
// Start each of the threads. They will block until the latch is released.
This is
// necessary because it takes some time for the threads to each allocate
their required
// system resources and actually be ready to run.
int threadCount = m_threads.length;
for( int i = 0; i < threadCount; i++ )
{
m_threads[ i ].start();
}
// Wait for all of the threads to start before starting to time the test
synchronized( m_semaphore )
{
while( m_startedCount < threadCount )
{
m_semaphore.wait();
}
// Start clean
resetMemory();
// Release the threads.
m_latched = true;
getLogger().debug( "Main thread released the test thread latch." );
m_semaphore.notifyAll();
}
// Start timing
long startTime = System.currentTimeMillis();
// Wait for all of the threads to complete
synchronized( m_semaphore )
{
getLogger().debug( "Waiting for test threads to all complete." );
while( m_completedCount < threadCount )
{
try
{
m_semaphore.wait();
}
catch( InterruptedException e )
{
}
}
}
final long duration = System.currentTimeMillis() - startTime;
getLogger().debug( "All test threads completed." );
if( m_exception != null )
{
throw new CascadingAssertionFailedError( "Exception in test thread.",
m_exception );
}
return duration;
}
/**
* Inner access method to getLogger() to work around a bug in the Javac compiler
* when getLogger() is called from the method of an inner class. Jikes seems to
* handle it Ok. :-/
*/
private Logger getInnerLogger()
{
return getLogger();
}
/*---------------------------------------------------------------
* Inner Classes
*-------------------------------------------------------------*/
private class Runner extends Thread
{
private Runnable m_runnable;
protected Runner( Runnable runnable, String name )
{
super( name );
m_runnable = runnable;
}
public void run()
{
try
{
// Need all threads to wait until all the others are ready.
synchronized( m_semaphore )
{
m_startedCount++;
getInnerLogger().debug( "Started " + m_startedCount + " test
threads." );
if( m_startedCount >= m_threads.length )
{
m_semaphore.notifyAll();
}
while( !m_latched )
{
try
{
m_semaphore.wait();
}
catch( InterruptedException e )
{
}
}
}
// Run the runnable
try
{
m_runnable.run();
}
catch( Throwable t )
{
synchronized( m_semaphore )
{
getInnerLogger().error( "Error in " +
Thread.currentThread().getName(), t );
if( m_exception != null )
{
m_exception = t;
}
}
}
}
finally
{
// Say that we are done
synchronized( m_semaphore )
{
m_completedCount++;
getInnerLogger().debug( m_completedCount + " test threads
completed." );
m_semaphore.notifyAll();
}
}
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]