You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by mc...@apache.org on 2004/03/15 10:56:25 UTC
cvs commit: avalon-excalibur/thread/instrumented/src/java/org/apache/avalon/excalibur/thread/impl InstrumentedResourceLimitingThreadPool.java
mcconnell 2004/03/15 01:56:25
Added: thread/api .cvsignore project.xml
thread/api/src/java/org/apache/excalibur/thread
Executable.java ThreadControl.java ThreadPool.java
thread/impl .cvsignore project.properties project.xml
thread/impl/src/java/org/apache/avalon/excalibur/thread/impl
BasicThreadPool.java DefaultThreadPool.java
ExecutableExecuteable.java
ResourceLimitingThreadPool.java
SimpleWorkerThread.java
thread/impl/src/java/org/apache/excalibur/thread/impl
AbstractThreadPool.java DefaultThreadControl.java
ExecutableRunnable.java WorkerThread.java
thread/impl/src/test/org/apache/avalon/excalibur/thread/impl/test
BufferedLogger.java DefaultThreadPoolTestCase.java
ResourceLimitingThreadPoolTestCase.java
thread/instrumented .cvsignore project.xml
thread/instrumented/src/java/org/apache/avalon/excalibur/thread/impl
InstrumentedResourceLimitingThreadPool.java
Log:
1. created an api/impl/instrumented package separation
- stripped out instrumentable support in
ResourceLimitingThreadPool
- added InstrumentedResourceLimitingThreadPool under
instrumented package that uses the
InstrumentedResourceLimitingPool as the backing pool
2. removed deprecated ThreadControl and ThreadPool
3. removed references to deprecated classes in
BasicThreadPool and un-deprecated the class
4. un-deprecated ExecutableExecutable as it is used by
BasicThreadPool but BasicThreadPool does not appear
to have a replacement and is used in non-deprecated
content
5. removed dependence on excalibur component testcase
6. set version on all artifacts to SNAPSHOT
Revision Changes Path
1.1 avalon-excalibur/thread/api/.cvsignore
Index: .cvsignore
===================================================================
maven.log
velocity.log
build.properties
target
1.1 avalon-excalibur/thread/api/project.xml
Index: project.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../../project.xml</extend>
<groupId>excalibur-thread</groupId>
<id>excalibur-thread-api</id>
<name>Excalibur Thread API</name>
<currentVersion>SNAPSHOT</currentVersion>
<package>org.apache.avalon.excalibur.thread</package>
<inceptionYear>2001</inceptionYear>
<shortDescription>Excalibur Threads API</shortDescription>
<dependencies>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework-api</artifactId>
<version>4.1.5</version>
</dependency>
</dependencies>
</project>
1.1 avalon-excalibur/thread/api/src/java/org/apache/excalibur/thread/Executable.java
Index: Executable.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread;
/**
* The Executable can be implemented by components that need to perform
* some work. In many respects it is similar to Runnable except that it
* also allows an application to throw a non-Runtime Exception.
*
* <p>The work done may be short lived (ie a simple task) or it could
* be a long running.</p>
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public interface Executable
{
/**
* Execute the action associated with this component.
*
* @throws Exception if an error occurs
*/
void execute()
throws Exception;
}
1.1 avalon-excalibur/thread/api/src/java/org/apache/excalibur/thread/ThreadControl.java
Index: ThreadControl.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread;
/**
* This interface defines the method through which Threads can be controller.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public interface ThreadControl
{
/**
* Wait for specified time for thread to complete it's work.
*
* @param milliSeconds the duration in milliseconds to wait until the thread has finished work
* @throws IllegalStateException if isValid() == false
* @throws InterruptedException if another thread has interrupted the current thread.
* The interrupted status of the current thread is cleared when this exception
* is thrown.
*/
void join( long milliSeconds )
throws IllegalStateException, InterruptedException;
/**
* Call {@link Thread#interrupt()} on thread being controlled.
*
* @throws IllegalStateException if isValid() == false
* @throws SecurityException if caller does not have permission to call interupt()
*/
void interrupt()
throws IllegalStateException, SecurityException;
/**
* Determine if thread has finished execution
*
* @return true if thread is finished, false otherwise
*/
boolean isFinished();
/**
* Retrieve throwable that caused thread to cease execution.
* Only valid when true == isFinished()
*
* @return the throwable that caused thread to finish execution
*/
Throwable getThrowable();
}
1.1 avalon-excalibur/thread/api/src/java/org/apache/excalibur/thread/ThreadPool.java
Index: ThreadPool.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread;
/**
* This class is the public frontend for the thread pool code.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public interface ThreadPool
{
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
ThreadControl execute( Runnable work );
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
ThreadControl execute( Executable work );
}
1.1 avalon-excalibur/thread/impl/.cvsignore
Index: .cvsignore
===================================================================
maven.log
velocity.log
build.properties
target
1.1 avalon-excalibur/thread/impl/project.properties
Index: project.properties
===================================================================
maven.compile.deprecation = true
1.1 avalon-excalibur/thread/impl/project.xml
Index: project.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../../project.xml</extend>
<groupId>excalibur-thread</groupId>
<id>excalibur-thread-impl</id>
<name>Excalibur Thread Implementation</name>
<currentVersion>SNAPSHOT</currentVersion>
<package>org.apache.avalon.excalibur.thread</package>
<inceptionYear>2001</inceptionYear>
<shortDescription>Excalibur Threads Implementation</shortDescription>
<dependencies>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework-api</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework-impl</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>excalibur-thread</groupId>
<artifactId>excalibur-thread-api</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>excalibur-pool</groupId>
<artifactId>excalibur-pool-api</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>excalibur-pool</groupId>
<artifactId>excalibur-pool-impl</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>concurrent</groupId>
<artifactId>concurrent</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>
1.1 avalon-excalibur/thread/impl/src/java/org/apache/avalon/excalibur/thread/impl/BasicThreadPool.java
Index: BasicThreadPool.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl;
import org.apache.avalon.excalibur.pool.ObjectFactory;
import org.apache.avalon.excalibur.pool.Pool;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.excalibur.thread.ThreadPool;
import org.apache.excalibur.thread.ThreadControl;
import org.apache.excalibur.thread.impl.AbstractThreadPool;
import org.apache.excalibur.thread.impl.WorkerThread;
/**
* The ThreadPool that binds to Legacy Pooling implementation.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
class BasicThreadPool
extends AbstractThreadPool
implements ObjectFactory, LogEnabled, Disposable, ThreadPool
{
/**
* The underlying pool.
*/
private Pool m_pool;
/**
* The logger to use for debugging purposes.
*/
private Logger m_logger;
/**
* Create a new ThreadPool with specified capacity.
*
* @param threadGroup the thread group used in pool
* @param name the name of pool (used in naming threads)
* @param pool the underling pool
* @throws Exception if unable to create pool
*/
public BasicThreadPool( final ThreadGroup threadGroup,
final String name,
final Pool pool )
throws Exception
{
super( name, threadGroup );
if( null == pool )
{
throw new NullPointerException( "pool" );
}
m_pool = pool;
}
/**
* Setup Logging.
*
* @param logger the logger
*/
public void enableLogging( final Logger logger )
{
m_logger = logger;
ContainerUtil.enableLogging( m_pool, logger );
}
/**
* Dispose of underlying pool and cleanup resources.
*/
public void dispose()
{
ContainerUtil.dispose( m_pool );
m_pool = null;
}
/**
* Create new Poolable instance.
*
* @return the new Poolable instance
*/
public Object newInstance()
{
return createWorker();
}
/**
* Overide newWorkerThread to provide a WorkerThread
* that is Poolable and LogEnabled.
*
* @param name the name of WorkerThread
* @return the created WorkerThread
*/
protected WorkerThread newWorkerThread( final String name )
{
final SimpleWorkerThread thread =
new SimpleWorkerThread( this, getThreadGroup(), name );
ContainerUtil.enableLogging( thread, m_logger.getChildLogger( "worker" ) );
return thread;
}
public void decommission( final Object object )
{
if( object instanceof WorkerThread )
{
destroyWorker( (WorkerThread)object );
}
}
/**
* Return the class of poolable instance.
*
* @return the class of poolable instance.
*/
public Class getCreatedClass()
{
return SimpleWorkerThread.class;
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Executable work )
{
return execute( new ExecutableExecuteable( work ) );
}
/**
* Retrieve a worker thread from pool.
*
* @return the worker thread retrieved from pool
*/
protected WorkerThread getWorker()
{
try
{
return (WorkerThread)m_pool.get();
}
catch( final Exception e )
{
final String message =
"Unable to access thread pool due to " + e;
throw new IllegalStateException( message );
}
}
/**
* Release worker back into pool.
*
* FIX ME: do we want to verify if it is interrupted or interrupt the worker
* thread?
*
* @param worker the worker (Should be a {@link SimpleWorkerThread}).
*/
protected void releaseWorker( final WorkerThread worker )
{
worker.clearInterruptFlag();
// If the pool is disposed before the last worker has been released
// m_pool will be null. This can be difficult to avoid as there
// is no way to query whether or not all workers have actually been
// released. Underlying pool implementations should probably block
// on their dispose methods until all outstanding objects have been
// returned.
Pool pool = m_pool; // Be thread safe
if ( pool != null )
{
pool.put( (SimpleWorkerThread)worker );
}
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/avalon/excalibur/thread/impl/DefaultThreadPool.java
Index: DefaultThreadPool.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl;
import org.apache.avalon.excalibur.pool.ObjectFactory;
import org.apache.avalon.excalibur.pool.SoftResourceLimitingPool;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.excalibur.thread.ThreadControl;
import org.apache.excalibur.thread.ThreadPool;
/**
* This class is the public frontend for the thread pool code.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public class DefaultThreadPool
extends ThreadGroup
implements ObjectFactory, LogEnabled, Disposable, ThreadPool
{
private final BasicThreadPool m_pool;
private SoftResourceLimitingPool m_underlyingPool;
public DefaultThreadPool( final int capacity )
throws Exception
{
this( "Worker Pool", capacity );
}
public DefaultThreadPool( final String name,
final int capacity )
throws Exception
{
super( name );
m_underlyingPool = new SoftResourceLimitingPool( this, capacity );
m_pool = new BasicThreadPool( this, name, m_underlyingPool );
}
public DefaultThreadPool( final String name,
final int min,
final int max )
throws Exception
{
super( name );
m_underlyingPool = new SoftResourceLimitingPool( this, min, max );
m_pool = new BasicThreadPool( this, name, m_underlyingPool );
}
public void enableLogging( final Logger logger )
{
ContainerUtil.enableLogging( m_pool, logger );
}
public void dispose()
{
m_pool.dispose();
}
public Object newInstance()
{
return m_pool.newInstance();
}
public void decommission( final Object object )
{
m_pool.decommission( object );
}
public Class getCreatedClass()
{
return m_pool.getCreatedClass();
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Executable work )
{
return m_pool.execute( work );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Runnable work )
{
return m_pool.execute( work );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final org.apache.excalibur.thread.Executable work )
{
return m_pool.execute( work );
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/avalon/excalibur/thread/impl/ExecutableExecuteable.java
Index: ExecutableExecuteable.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl;
import org.apache.excalibur.thread.Executable;
/**
* Class to adapt a {@link org.apache.avalon.framework.activity.Executable} object in
* an {@link Executable} object.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
final class ExecutableExecuteable
implements Executable
{
///The runnable instance being wrapped
private org.apache.avalon.framework.activity.Executable m_executable;
/**
* Create adapter using specified executable.
*
* @param executable the executable to adapt to
*/
protected ExecutableExecuteable( final org.apache.avalon.framework.activity.Executable executable )
{
if( null == executable )
{
throw new NullPointerException( "executable" );
}
m_executable = executable;
}
/**
* Execute the underlying
* {@link org.apache.avalon.framework.activity.Executable} object.
*
* @throws Exception if an error occurs
*/
public void execute()
throws Exception
{
m_executable.execute();
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/avalon/excalibur/thread/impl/ResourceLimitingThreadPool.java
Index: ResourceLimitingThreadPool.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl;
import org.apache.avalon.excalibur.pool.ObjectFactory;
import org.apache.avalon.excalibur.pool.ResourceLimitingPool;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.excalibur.thread.ThreadControl;
import org.apache.excalibur.thread.ThreadPool;
/*
*
* A Thread Pool which can be configured to have a hard limit on the maximum number of threads
* which will be allocated. This is very important for servers to avoid running out of system
* resources. The pool can be configured to block for a new thread or throw an exception.
* The maximum block time can also be set.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
* @version CVS $Revision: 1.1 $ $Date: 2004/03/15 09:56:24 $
* @since 4.1
*/
public class ResourceLimitingThreadPool
extends ThreadGroup
implements ObjectFactory, LogEnabled, Disposable, ThreadPool
{
private ResourceLimitingPool m_underlyingPool;
/**
* The associated thread pool.
*/
private BasicThreadPool m_pool;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
/**
* Creates a new <code>ResourceLimitingThreadPool</code>.
*
* @param max Maximum number of Poolables which can be stored in the pool, 0 implies no limit.
*/
public ResourceLimitingThreadPool( final int max )
{
this( "Worker Pool", max );
}
/**
* Creates a new <code>ResourceLimitingThreadPool</code> with maxStrict enabled,
* blocking enabled, no block timeout and a trim interval of 10 seconds.
*
* @param name Name which will used as the thread group name as well as the prefix of the
* names of all threads created by the pool.
* @param max Maximum number of WorkerThreads which can be stored in the pool,
* 0 implies no limit.
*/
public ResourceLimitingThreadPool( final String name, final int max )
{
this( name, max, true, true, 0, 10000 );
}
/**
* Creates a new <code>ResourceLimitingThreadPool</code>.
*
* @param name Name which will used as the thread group name as well as the prefix of the
* names of all threads created by the pool.
* @param max Maximum number of WorkerThreads which can be stored in the pool,
* 0 implies no limit.
* @param maxStrict true if the pool should never allow more than max WorkerThreads to
* be created. Will cause an exception to be thrown if more than max WorkerThreads are
* requested and blocking is false.
* @param blocking true if the pool should cause a thread calling get() to block when
* WorkerThreads are not currently available on the pool.
* @param blockTimeout The maximum amount of time, in milliseconds, that a call to get() will
* block before an exception is thrown. A value of 0 implies an indefinate wait.
* @param trimInterval The minimum interval with which old unused WorkerThreads will be
* removed from the pool. A value of 0 will cause the pool to never trim WorkerThreads.
*/
public ResourceLimitingThreadPool( final String name,
final int max,
final boolean maxStrict,
final boolean blocking,
final long blockTimeout,
final long trimInterval )
{
super( name );
m_underlyingPool =
new ResourceLimitingPool( this, max, maxStrict,
blocking, blockTimeout,
trimInterval );
try
{
m_pool = new BasicThreadPool( this, name, m_underlyingPool );
}
catch( Exception e )
{
final String message = "Unable to create ThreadPool due to " + e;
throw new IllegalStateException( message );
}
}
/**
* Return the number of worker threads in the pool.
*
* @return the numebr of worker threads in the pool.
*/
public int getSize()
{
return m_underlyingPool.getSize();
}
public void enableLogging( final Logger logger )
{
ContainerUtil.enableLogging( m_pool, logger );
}
public void dispose()
{
m_pool.dispose();
}
public Object newInstance()
{
return m_pool.newInstance();
}
public void decommission( final Object object )
{
m_pool.decommission( object );
}
public Class getCreatedClass()
{
return m_pool.getCreatedClass();
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Executable work )
{
return m_pool.execute( work );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Runnable work )
{
return m_pool.execute( work );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final org.apache.excalibur.thread.Executable work )
{
return m_pool.execute( work );
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/avalon/excalibur/thread/impl/SimpleWorkerThread.java
Index: SimpleWorkerThread.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl;
import org.apache.avalon.excalibur.pool.Poolable;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.excalibur.thread.impl.AbstractThreadPool;
import org.apache.excalibur.thread.impl.WorkerThread;
/**
* This class extends the Thread class to add recyclable functionalities.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
class SimpleWorkerThread
extends WorkerThread
implements Poolable, LogEnabled
{
/** Log major events like uncaught exceptions and worker creation
* and deletion. Stuff that is useful to be able to see over long
* periods of time. */
private Logger m_logger;
/**
* Log minor detail events like
*/
private Logger m_detailLogger;
/**
* Allocates a new <code>Worker</code> object.
*/
protected SimpleWorkerThread( final AbstractThreadPool pool,
final ThreadGroup group,
final String name )
{
super( pool, group, name );
}
public void enableLogging( final Logger logger )
{
m_logger = logger;
m_detailLogger = logger.getChildLogger( "detail" );
// Log a created message here rather as we can't in the constructor
// due to the lack of a logger.
debug( "created." );
}
/**
* Used to log major events against the worker. Creation, deletion,
* uncaught exceptions etc.
*
* @param message Message to log.
*/
protected void debug( final String message )
{
if ( m_logger.isDebugEnabled() )
{
// As we are dealing with threads where more than one thread is
// always involved, log both the name of the thread that triggered
// event along with the name of the worker involved. This
// increases the likely hood of walking away sane after a
// debugging session.
m_logger.debug( "\"" + getName() + "\" "
+ "(in " + Thread.currentThread().getName() + ") : " + message );
}
}
/**
* Used to log major events against the worker. Creation, deletion,
* uncaught exceptions etc.
*
* @param message Message to log.
* @param throwable Throwable to log with the message.
*/
protected void debug( final String message, final Throwable throwable )
{
if ( m_logger.isDebugEnabled() )
{
m_logger.debug( "\"" + getName() + "\" "
+ "(in " + Thread.currentThread().getName() + ") : " + message, throwable );
}
}
/**
* Used to log minor events against the worker. Start and stop of
* individual pieces of work etc. Separated from the major events
* so that they are not lost in a sea of minor events.
*
* @param message Message to log.
*/
protected void detailDebug( final String message )
{
if ( m_detailLogger.isDebugEnabled() )
{
m_detailLogger.debug( "\"" + getName() + "\" "
+ "(in " + Thread.currentThread().getName() + ") : " + message );
}
}
/**
* Used to log minor events against the worker. Start and stop of
* individual pieces of work etc. Separated from the major events
* so that they are not lost in a sea of minor events.
*
* @param message Message to log.
* @param throwable Throwable to log with the message.
*/
protected void detailDebug( final String message, final Throwable throwable )
{
if ( m_detailLogger.isDebugEnabled() )
{
m_detailLogger.debug( "\"" + getName() + "\" "
+ "(in " + Thread.currentThread().getName() + ") : " + message, throwable );
}
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/excalibur/thread/impl/AbstractThreadPool.java
Index: AbstractThreadPool.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread.impl;
import org.apache.excalibur.thread.Executable;
import org.apache.excalibur.thread.ThreadControl;
import org.apache.excalibur.thread.ThreadPool;
/**
* This is the base class of all ThreadPools.
* Sub-classes should implement the abstract methods to
* retrieve and return Threads to the pool.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public abstract class AbstractThreadPool
implements ThreadPool
{
/**
* The thread group associated with pool.
*/
private final ThreadGroup m_threadGroup;
/**
* The name of the thread pool.
* Used in naming threads.
*/
private final String m_name;
/**
* A Running number that indicates the number
* of threads created by pool. Starts at 0 and
* increases.
*/
private int m_level;
/**
* Create a ThreadPool with the specified name.
*
* @param name the name of thread pool (appears in thread group
* and thread names)
* @throws Exception if unable to create pool
*/
public AbstractThreadPool( final String name,
final ThreadGroup threadGroup )
throws Exception
{
if( null == name )
{
throw new NullPointerException( "name" );
}
if( null == threadGroup )
{
throw new NullPointerException( "threadGroup" );
}
m_name = name;
m_threadGroup = threadGroup;
}
/**
* Destroy a worker thread by scheduling it for shutdown.
*
* @param thread the worker thread
*/
protected void destroyWorker( final WorkerThread thread )
{
thread.dispose();
}
/**
* Create a WorkerThread and start it up.
*
* @return the worker thread.
*/
protected WorkerThread createWorker()
{
final String name = m_name + " Worker #" + m_level++;
final WorkerThread worker = newWorkerThread( name );
worker.setDaemon( true );
worker.start();
return worker;
}
/**
* Create a new worker for pool.
*
* @param name the name of worker
* @return the new WorkerThread
*/
protected WorkerThread newWorkerThread( final String name )
{
return new WorkerThread( this, m_threadGroup, name );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Runnable work )
{
return execute( new ExecutableRunnable( work ) );
}
/**
* Execute some executable work in a thread.
*
* @param work the work
* @return the ThreadControl
*/
public ThreadControl execute( final Executable work )
{
final WorkerThread worker = getWorker();
return worker.execute( work );
}
/**
* Get the name used for thread pool.
* (Used in naming threads).
*
* @return the thread pool name
*/
protected String getName()
{
return m_name;
}
/**
* Return the thread group that thread pool is associated with.
*
* @return the thread group that thread pool is associated with.
*/
protected ThreadGroup getThreadGroup()
{
return m_threadGroup;
}
/**
* Retrieve a worker thread from pool.
*
* @return the worker thread retrieved from pool
*/
protected abstract WorkerThread getWorker();
/**
* Return the WorkerThread to the pool.
*
* @param worker the worker thread to put back in pool
*/
protected abstract void releaseWorker( final WorkerThread worker );
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/excalibur/thread/impl/DefaultThreadControl.java
Index: DefaultThreadControl.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread.impl;
import org.apache.excalibur.thread.ThreadControl;
/**
* Default implementation of ThreadControl interface.
* Is used by worker thread to supply control information to the
* clients of thread pool.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
final class DefaultThreadControl
implements ThreadControl
{
///Thread that this control is associated with
private Thread m_thread;
///Throwable that caused thread to terminate
private Throwable m_throwable;
/**
* Construct thread control for a specific thread.
*
* @param thread the thread to control
*/
protected DefaultThreadControl( final Thread thread )
{
m_thread = thread;
}
/**
* Wait for specified time for thread to complete it's work.
*
* @param milliSeconds the duration in milliseconds to wait until the thread has finished work
* @throws java.lang.IllegalStateException if isValid() == false
* @throws java.lang.InterruptedException if another thread has interrupted the current thread.
* The interrupted status of the current thread is cleared when this exception
* is thrown.
*/
public synchronized void join( final long milliSeconds )
throws IllegalStateException, InterruptedException
{
if (! isFinished() )
{
m_thread.join(milliSeconds);
}
}
public void interupt()
throws IllegalStateException, SecurityException
{
interrupt();
}
/**
* Call Thread.interrupt() on thread being controlled.
*
* @throws IllegalStateException if isValid() == false
* @throws SecurityException if caller does not have permission to call interupt()
*/
public synchronized void interrupt()
throws IllegalStateException, SecurityException
{
if( !isFinished() )
{
m_thread.interrupt();
}
}
/**
* Determine if thread has finished execution
*
* @return true if thread is finished, false otherwise
*/
public synchronized boolean isFinished()
{
return ( null == m_thread );
}
/**
* Retrieve throwable that caused thread to cease execution.
* Only valid when true == isFinished()
*
* @return the throwable that caused thread to finish execution
*/
public Throwable getThrowable()
{
return m_throwable;
}
/**
* Method called by thread to release control.
*
* @param throwable Throwable that caused thread to complete (may be null)
*/
protected synchronized void finish( final Throwable throwable )
{
m_thread = null;
m_throwable = throwable;
notifyAll();
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/excalibur/thread/impl/ExecutableRunnable.java
Index: ExecutableRunnable.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread.impl;
import org.apache.excalibur.thread.Executable;
/**
* Class to adapt a {@link Runnable} object in
* an {@link Executable} object.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
final class ExecutableRunnable
implements Executable
{
///The runnable instance being wrapped
private Runnable m_runnable;
/**
* Create adapter using specified runnable.
*
* @param runnable the runnable to adapt to
*/
protected ExecutableRunnable( final Runnable runnable )
{
if( null == runnable )
{
throw new NullPointerException( "runnable" );
}
m_runnable = runnable;
}
/**
* Execute the underlying {@link Runnable} object.
*
* @throws Exception if an error occurs
*/
public void execute()
throws Exception
{
m_runnable.run();
}
}
1.1 avalon-excalibur/thread/impl/src/java/org/apache/excalibur/thread/impl/WorkerThread.java
Index: WorkerThread.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.excalibur.thread.impl;
import org.apache.excalibur.thread.Executable;
import org.apache.excalibur.thread.ThreadControl;
/**
* This class extends the Thread class to add recyclable functionalities.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public class WorkerThread
extends Thread
{
/**
* Enables debug output of major events. Subclasses which implement
* their own logging do not require this to be true.
*/
private static final boolean ENABLE_DEBUG = false;
/**
* Enables debug output of minor events. Subclasses which implement
* their own logging do not require this to be true.
*/
private static final boolean ENABLE_DETAIL_DEBUG = false;
/**
* The work currentlyy associated with worker (May be null).
*/
private Executable m_work;
/**
* The thread control associated with current work.
* Should be null if work is null.
*/
private DefaultThreadControl m_threadControl;
/**
* True if this thread is alive and not scheduled for shutdown.
*/
private boolean m_alive;
/**
* True if this thread needs to clear the interrupt flag
*/
private boolean m_clearInterruptFlag;
/**
* The thread pool this thread is associated with.
*/
private final AbstractThreadPool m_pool;
/**
* Allocates a new <code>Worker</code> object.
*/
protected WorkerThread( final AbstractThreadPool pool,
final ThreadGroup group,
final String name )
{
super( group, "" );
if( null == name )
{
throw new NullPointerException( "name" );
}
if( null == pool )
{
throw new NullPointerException( "pool" );
}
setName( name );
m_work = null;
m_alive = true;
m_clearInterruptFlag = false;
m_pool = pool;
setDaemon( false );
}
/**
* The main execution loop.
*/
public final synchronized void run()
{
debug( "starting." );
try
{
while( m_alive )
{
waitForWork();
if ( m_alive )
{
detailDebug( "start with work: " + m_work );
try
{
try
{
preExecute();
// Actually do the work.
m_work.execute();
// Completed without error, so notify the thread control
m_threadControl.finish( null );
}
catch( final ThreadDeath threadDeath )
{
debug( "thread has died." );
m_threadControl.finish( threadDeath );
// This is to let the thread death propagate to the runtime
// enviroment to let it know it must kill this worker
throw threadDeath;
}
catch( final Throwable throwable )
{
// Error thrown while working.
debug( "error caught", throwable );
m_threadControl.finish( throwable );
}
}
finally
{
detailDebug( "done with work: " + m_work );
m_work = null;
m_threadControl = null;
if ( m_clearInterruptFlag )
{
clearInterruptFlag();
}
postExecute();
}
/*
try
{
preExecute();
m_work.execute();
if (m_clearInterruptFlag) clearInterruptFlag();
m_threadControl.finish( null );
}
catch( final ThreadDeath threadDeath )
{
debug( "thread has died." );
m_threadControl.finish( threadDeath );
// This is to let the thread death propagate to the runtime
// enviroment to let it know it must kill this worker
throw threadDeath;
}
catch( final Throwable throwable )
{
// Error thrown while working.
debug( "error caught", throwable );
m_threadControl.finish( throwable );
}
finally
{
detailDebug( "done." );
m_work = null;
m_threadControl = null;
if (m_clearInterruptFlag) clearInterruptFlag();
postExecute();
}
*/
//should this be just notify or notifyAll ???
//It seems to resource intensive option to use notify()
//notifyAll();
// Let the thread, if any, waiting for the work to complete
// know we are done. Should never me more than one thread
// waiting, so notifyAll is not necessary.
notify();
// recycle ourselves
recycleThread();
}
}
}
finally
{
debug( "stopped." );
}
}
/**
* Implement this method to replace thread back into pool.
*/
protected void recycleThread()
{
if ( !m_alive )
{
throw new IllegalStateException( "Attempted to recycle dead thread." );
}
detailDebug( "recycle." );
if ( m_clearInterruptFlag )
{
clearInterruptFlag();
}
m_pool.releaseWorker( this );
}
/**
* Overide this method to execute something after
* each bit of "work".
*/
protected void postExecute()
{
}
/**
* Overide this method to execute something before
* each bit of "work".
*/
protected void preExecute()
{
clearInterruptFlag();
//TODO: Thread name setting should reuse the
//ThreadContext code if ThreadContext used.
}
/**
* Clears the interrupt flag for this thread. Since Java does
* not provide a method that does this for an external thread,
* we have to verify that we are in the WorkerThread. If the
* code calling this method does not originate from this thread,
* we set a flag and wait for it to be called internally.
*/
public void clearInterruptFlag()
{
if (Thread.currentThread().equals(this))
{
Thread.interrupted();
m_clearInterruptFlag = false;
}
else
{
m_clearInterruptFlag = true;
}
}
/**
* Set the <tt>alive</tt> variable to false causing the worker to die.
* If the worker is stalled and a timeout generated this call, this method
* does not change the state of the worker (that must be destroyed in other
* ways).
* <p>
* This is called by the pool when it is removed.
*/
public void dispose()
{
debug( "destroying." );
m_alive = false;
// Notify the thread so it will be woken up and notice that it has been destroyed.
synchronized(this)
{
this.notify();
}
debug( "destroyed." );
}
/**
* Set the <tt>Work</tt> code this <tt>Worker</tt> must
* execute and <i>notifies</i> its thread to do it.
*/
protected synchronized ThreadControl execute( final Executable work )
{
m_work = work;
m_threadControl = new DefaultThreadControl( this );
detailDebug( "notifying this worker of new work: " + work.toString() );
notify();
return m_threadControl;
}
/**
* Set the <tt>Work</tt> code this <tt>Worker</tt> must
* execute and <i>notifies</i> its thread to do it. Wait
* until the executable has finished before returning.
*/
protected void executeAndWait( final Executable work )
{
// Assign work to the thread.
execute( work );
// Wait for the work to complete.
synchronized(this)
{
while( null != m_work )
{
try
{
detailDebug( "waiting for work to complete." );
wait();
detailDebug( "notified." );
}
catch( final InterruptedException ie )
{
// Ignore
}
}
}
}
/**
* For for new work to arrive or for the thread to be destroyed.
*/
private void waitForWork()
{
synchronized(this)
{
while( m_alive && ( null == m_work ) )
{
try
{
detailDebug( "waiting for work." );
wait();
detailDebug( "notified." );
}
catch( final InterruptedException ie )
{
// Ignore
}
}
}
}
/**
* Used to log major events against the worker. Creation, deletion,
* uncaught exceptions etc.
* <p>
* This implementation is a Noop. Subclasses can override to actually
* do some logging.
*
* @param message Message to log.
*/
protected void debug( final String message )
{
if( ENABLE_DEBUG )
{
System.out.println( getName() + ": " + message );
}
}
/**
* Used to log major events against the worker. Creation, deletion,
* uncaught exceptions etc.
* <p>
* This implementation is a Noop. Subclasses can override to actually
* do some logging.
*
* @param message Message to log.
* @param throwable Throwable to log with the message.
*/
protected void debug( final String message, final Throwable throwable )
{
if( ENABLE_DEBUG )
{
System.out.println( getName() + ": " + message + ": " + throwable );
}
}
/**
* Used to log minor events against the worker. Start and stop of
* individual pieces of work etc. Separated from the major events
* so that they are not lost in a sea of minor events.
* <p>
* This implementation is a Noop. Subclasses can override to actually
* do some logging.
*
* @param message Message to log.
*/
protected void detailDebug( final String message )
{
if ( ENABLE_DETAIL_DEBUG )
{
System.out.println( getName() + ": " + message );
}
}
/**
* Used to log minor events against the worker. Start and stop of
* individual pieces of work etc. Separated from the major events
* so that they are not lost in a sea of minor events.
* <p>
* This implementation is a Noop. Subclasses can override to actually
* do some logging.
*
* @param message Message to log.
* @param throwable Throwable to log with the message.
*/
protected void detailDebug( final String message, final Throwable throwable )
{
if ( ENABLE_DETAIL_DEBUG )
{
System.out.println( getName() + ": " + message + ": " + throwable );
}
}
}
1.1 avalon-excalibur/thread/impl/src/test/org/apache/avalon/excalibur/thread/impl/test/BufferedLogger.java
Index: BufferedLogger.java
===================================================================
/*
* Copyright 2002-2004 Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl.test;
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:dev@avalon.apache.org">Avalon Development Team</a>
* @version CVS $Revision: 1.1 $ $Date: 2004/03/15 09:56:24 $
* @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/thread/impl/src/test/org/apache/avalon/excalibur/thread/impl/test/DefaultThreadPoolTestCase.java
Index: DefaultThreadPoolTestCase.java
===================================================================
/*
* Copyright 2002-2004 Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl.test;
import junit.framework.TestCase;
import org.apache.avalon.excalibur.thread.impl.DefaultThreadPool;
import org.apache.avalon.framework.logger.ConsoleLogger;
/**
* TestCase for DefaultThreadPool.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
*/
public class DefaultThreadPoolTestCase
extends TestCase
{
public DefaultThreadPoolTestCase( final String name )
{
super( name );
}
public void testWithThreadContext()
throws Exception
{
final DefaultThreadPool pool = new DefaultThreadPool( "default", 10 );
pool.setDaemon( false );
pool.enableLogging( new ConsoleLogger( ConsoleLogger.LEVEL_INFO ) );
pool.execute( new DummyRunnable() );
}
public void testWithoutThreadContext()
throws Exception
{
final DefaultThreadPool pool = new DefaultThreadPool( "default", 10 );
pool.setDaemon( false );
pool.enableLogging( new ConsoleLogger( ConsoleLogger.LEVEL_INFO ) );
pool.execute( new DummyRunnable() );
}
private static class DummyRunnable
implements Runnable
{
public void run()
{
}
}
}
1.1 avalon-excalibur/thread/impl/src/test/org/apache/avalon/excalibur/thread/impl/test/ResourceLimitingThreadPoolTestCase.java
Index: ResourceLimitingThreadPoolTestCase.java
===================================================================
/*
* Copyright 2002-2004 Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl.test;
import junit.framework.TestCase;
import org.apache.avalon.excalibur.thread.impl.ResourceLimitingThreadPool;
/**
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
* @version CVS $Revision: 1.1 $ $Date: 2004/03/15 09:56:25 $
* @since 4.1
*/
public final class ResourceLimitingThreadPoolTestCase
extends TestCase
{
private volatile int m_completeCount;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
public ResourceLimitingThreadPoolTestCase()
{
this( "ResourceLimitingThreadPool Test Case" );
}
public ResourceLimitingThreadPoolTestCase( final String name )
{
super( name );
}
/*---------------------------------------------------------------
* Suite
*-------------------------------------------------------------*/
public void test1Worker1Task()
{
commonTest( 1, 1, 0L, 200L, 1, true, true, -1, -1 );
}
public void test1Worker5Tasks()
{
// One will start immediately, 4 will have to wait 200ms each in turn.
commonTest( 5, 1, 800L, 1000L, 1, true, true, -1, -1 );
}
public void test5Workers10Tasks()
{
// 5 will start immediately, 5 will have to wait 200ms for the first 5 to complete.
commonTest( 10, 5, 200L, 400L, 5, true, true, -1, -1 );
}
public void test10Workers100Tasks()
{
// 10 will start immediately, next 10 will have to wait 200ms for the
// first 10 to complete and so on.
commonTest( 100, 10, 1800L, 2000L, 10, true, true, -1, -1 );
}
public void test5Workers6TasksNoBlock()
{
commonTest( 6, 5, 0L, 200L, 5, true, false, -1, -1 );
}
public void test5Workers10TasksNotStrict()
{
commonTest( 10, 10, 0L, 200L, 5, false, false, -1, -1 );
}
protected void incCompleteCount()
{
synchronized( this )
{
m_completeCount++;
}
}
private void commonTest( int taskCount,
int firstSize,
long firstTime,
long totalTime,
int max,
boolean maxStrict,
boolean blocking,
long blockTimeout,
long trimInterval )
{
BufferedLogger logger = new BufferedLogger();
ResourceLimitingThreadPool pool = new ResourceLimitingThreadPool(
"Test Worker Pool", max, maxStrict, blocking, blockTimeout, trimInterval );
pool.enableLogging( logger );
Runnable runner = new Runnable()
{
public void run()
{
try
{
Thread.sleep( 200 );
}
catch( InterruptedException e )
{
}
incCompleteCount();
}
};
long start = System.currentTimeMillis();
m_completeCount = 0;
for( int i = 0; i < taskCount; i++ )
{
if( maxStrict && ( !blocking ) && i >= max )
{
// This request shoudl throw an exception.
try
{
pool.execute( runner );
fail( "Should have failed when requesting more than max resources." );
}
catch( Exception e )
{
// Ok
incCompleteCount();
}
}
else
{
pool.execute( runner );
}
}
long dur = System.currentTimeMillis() - start;
// Make sure that the size of the pool is what is expected.
assertEquals( "The pool size was not what it should be.", firstSize, pool.getSize() );
// Make sure this took about the right amount of time to get here.
//System.out.println( "First time: " + dur );
if( Math.abs( dur - firstTime ) > 500 )
{
fail( "Time to start all tasks, " + dur +
", was not within 500ms of the expected time, " + firstTime );
}
// Wait for all worker threads to complete.
while( m_completeCount < taskCount )
{
try
{
Thread.sleep( 10 );
}
catch( InterruptedException e )
{
}
}
dur = System.currentTimeMillis() - start;
// Make sure this took about the right amount of time to get here.
//System.out.println( "Total time: " + dur );
if( Math.abs( dur - totalTime ) > 500 )
{
fail( "Time to complete all tasks, " + dur +
", was not within 500ms of the expected time, " + totalTime );
}
//System.out.println( logger.toString() );
}
}
1.1 avalon-excalibur/thread/instrumented/.cvsignore
Index: .cvsignore
===================================================================
maven.log
velocity.log
build.properties
target
1.1 avalon-excalibur/thread/instrumented/project.xml
Index: project.xml
===================================================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>${basedir}/../../project.xml</extend>
<groupId>excalibur-thread</groupId>
<id>excalibur-thread-instrumented</id>
<name>Excalibur Thread Instrumented Implementation</name>
<currentVersion>SNAPSHOT</currentVersion>
<package>org.apache.avalon.excalibur.thread</package>
<inceptionYear>2001</inceptionYear>
<shortDescription>Excalibur Threads Instrumented Implementation</shortDescription>
<dependencies>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework-api</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework-impl</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>excalibur-thread</groupId>
<artifactId>excalibur-thread-api</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>excalibur-thread</groupId>
<artifactId>excalibur-thread-impl</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>excalibur-pool</groupId>
<artifactId>excalibur-pool-api</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>excalibur-pool</groupId>
<artifactId>excalibur-pool-impl</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>excalibur-pool</groupId>
<artifactId>excalibur-pool-instrumented</artifactId>
<version>SNAPSHOT</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>concurrent</groupId>
<artifactId>concurrent</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>excalibur-instrument</groupId>
<artifactId>excalibur-instrument</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
1.1 avalon-excalibur/thread/instrumented/src/java/org/apache/avalon/excalibur/thread/impl/InstrumentedResourceLimitingThreadPool.java
Index: InstrumentedResourceLimitingThreadPool.java
===================================================================
/*
* Copyright 2002-2004 The Apache Software Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.avalon.excalibur.thread.impl;
import org.apache.avalon.excalibur.pool.ObjectFactory;
import org.apache.avalon.excalibur.pool.InstrumentedResourceLimitingPool;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Executable;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.excalibur.instrument.Instrument;
import org.apache.excalibur.instrument.Instrumentable;
import org.apache.excalibur.thread.ThreadControl;
import org.apache.excalibur.thread.ThreadPool;
/**
* A Thread Pool which can be configured to have a hard limit on the maximum number of threads
* which will be allocated. This is very important for servers to avoid running out of system
* resources. The pool can be configured to block for a new thread or throw an exception.
* The maximum block time can also be set.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
* @version CVS $Revision: 1.1 $ $Date: 2004/03/15 09:56:25 $
* @since 4.1
*/
public class InstrumentedResourceLimitingThreadPool
extends ThreadGroup
implements ObjectFactory, LogEnabled, Disposable, ThreadPool, Instrumentable
{
private InstrumentedResourceLimitingPool m_underlyingPool;
/** Instrumentable Name assigned to this Instrumentable */
private String m_instrumentableName;
/**
* The associated thread pool.
*/
private BasicThreadPool m_pool;
/*---------------------------------------------------------------
* Constructors
*-------------------------------------------------------------*/
/**
* Creates a new <code>ResourceLimitingThreadPool</code>.
*
* @param max Maximum number of Poolables which can be stored in the pool, 0 implies no limit.
*/
public InstrumentedResourceLimitingThreadPool( final int max )
{
this( "Worker Pool", max );
}
/**
* Creates a new <code>ResourceLimitingThreadPool</code> with maxStrict enabled,
* blocking enabled, no block timeout and a trim interval of 10 seconds.
*
* @param name Name which will used as the thread group name as well as the prefix of the
* names of all threads created by the pool.
* @param max Maximum number of WorkerThreads which can be stored in the pool,
* 0 implies no limit.
*/
public InstrumentedResourceLimitingThreadPool( final String name, final int max )
{
this( name, max, true, true, 0, 10000 );
}
/**
* Creates a new <code>ResourceLimitingThreadPool</code>.
*
* @param name Name which will used as the thread group name as well as the prefix of the
* names of all threads created by the pool.
* @param max Maximum number of WorkerThreads which can be stored in the pool,
* 0 implies no limit.
* @param maxStrict true if the pool should never allow more than max WorkerThreads to
* be created. Will cause an exception to be thrown if more than max WorkerThreads are
* requested and blocking is false.
* @param blocking true if the pool should cause a thread calling get() to block when
* WorkerThreads are not currently available on the pool.
* @param blockTimeout The maximum amount of time, in milliseconds, that a call to get() will
* block before an exception is thrown. A value of 0 implies an indefinate wait.
* @param trimInterval The minimum interval with which old unused WorkerThreads will be
* removed from the pool. A value of 0 will cause the pool to never trim WorkerThreads.
*/
public InstrumentedResourceLimitingThreadPool( final String name,
final int max,
final boolean maxStrict,
final boolean blocking,
final long blockTimeout,
final long trimInterval )
{
super( name );
m_underlyingPool =
new InstrumentedResourceLimitingPool( this, max, maxStrict,
blocking, blockTimeout,
trimInterval );
try
{
m_pool = new BasicThreadPool( this, name, m_underlyingPool );
}
catch( Exception e )
{
final String message = "Unable to create ThreadPool due to " + e;
throw new IllegalStateException( message );
}
}
/*---------------------------------------------------------------
* Instrumentable Methods
*-------------------------------------------------------------*/
/**
* Sets the name for the Instrumentable. The Instrumentable Name is used
* to uniquely identify the Instrumentable during the configuration of
* the InstrumentManager and to gain access to an InstrumentableDescriptor
* through the InstrumentManager. The value should be a string which does
* not contain spaces or periods.
* <p>
* This value may be set by a parent Instrumentable, or by the
* InstrumentManager using the value of the 'instrumentable' attribute in
* the configuration of the component.
*
* @param name The name used to identify a Instrumentable.
*/
public void setInstrumentableName( String name )
{
m_instrumentableName = name;
}
/**
* Gets the name of the Instrumentable.
*
* @return The name used to identify a Instrumentable.
*/
public String getInstrumentableName()
{
return m_instrumentableName;
}
/**
* Obtain a reference to all the Instruments that the Instrumentable object
* wishes to expose. All sampling is done directly through the
* Instruments as opposed to the Instrumentable interface.
*
* @return An array of the Instruments available for profiling. Should
* never be null. If there are no Instruments, then
* EMPTY_INSTRUMENT_ARRAY can be returned. This should never be
* the case though unless there are child Instrumentables with
* Instruments.
*/
public Instrument[] getInstruments()
{
return Instrumentable.EMPTY_INSTRUMENT_ARRAY;
}
/**
* Any Object which implements Instrumentable can also make use of other
* Instrumentable child objects. This method is used to tell the
* InstrumentManager about them.
*
* @return An array of child Instrumentables. This method should never
* return null. If there are no child Instrumentables, then
* EMPTY_INSTRUMENTABLE_ARRAY can be returned.
*/
public Instrumentable[] getChildInstrumentables()
{
return new Instrumentable[]{m_underlyingPool};
}
/**
* Return the number of worker threads in the pool.
*
* @return the numebr of worker threads in the pool.
*/
public int getSize()
{
return m_underlyingPool.getSize();
}
public void enableLogging( final Logger logger )
{
ContainerUtil.enableLogging( m_pool, logger );
}
public void dispose()
{
m_pool.dispose();
}
public Object newInstance()
{
return m_pool.newInstance();
}
public void decommission( final Object object )
{
m_pool.decommission( object );
}
public Class getCreatedClass()
{
return m_pool.getCreatedClass();
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Executable work )
{
return m_pool.execute( work );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final Runnable work )
{
return m_pool.execute( work );
}
/**
* Run work in separate thread.
* Return a valid ThreadControl to control work thread.
*
* @param work the work to be executed.
* @return the ThreadControl
*/
public ThreadControl execute( final org.apache.excalibur.thread.Executable work )
{
return m_pool.execute( work );
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org