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