Author: giacomo
Date: Mon Nov  8 05:16:55 2004
New Revision: 56921

Added:
   cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/BoundedQueue.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ChannelWrapper.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultRunnableManager.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultThreadFactory.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultThreadPool.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/LinkedQueue.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/Queue.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/RunnableManager.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/SynchronousChannel.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ThreadFactory.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ThreadPool.java
Modified:
   cocoon/branches/BRANCH_2_1_X/gump.xml
   
cocoon/branches/BRANCH_2_1_X/src/blocks/cron/java/org/apache/cocoon/components/cron/QuartzJobScheduler.java
   cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/cron.js
   cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/docs.xml
   cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/conf/hsqldb.xconf
   
cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/java/org/apache/cocoon/components/hsqldb/ServerImpl.java
   
cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/coplet/adapter/impl/AbstractCopletAdapter.java
   
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java
   
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/JavaLanguage.java
   
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/Jikes.java
   cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
   
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/helpers/DefaultIncludeCacheManager.java
   cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf
   cocoon/branches/BRANCH_2_1_X/status.xml
Log:
Added replacement for Excalibur Event package in 
org.apache.cocoon.components.thread and migrated most
classes using their own threads to that package



Modified: cocoon/branches/BRANCH_2_1_X/gump.xml
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/gump.xml       (original)
+++ cocoon/branches/BRANCH_2_1_X/gump.xml       Mon Nov  8 05:16:55 2004
@@ -83,6 +83,7 @@
     <depend project="pizza"/>
     <depend project="junit"/>
     <depend project="eclipse-jtd"/>
+    <depend project="concurrent"/>
 
     <depend project="xmlunit"/><!-- used by the testcases -->
     <depend project="qdox"/>   <!-- used by ant tasks -->
@@ -1235,6 +1236,7 @@
     </ant>
 
     <depend project="cocoon" inherit="all"/>
+    <depend project="concurrent"/>
     
     <library name="pizza"/>
 

Modified: 
cocoon/branches/BRANCH_2_1_X/src/blocks/cron/java/org/apache/cocoon/components/cron/QuartzJobScheduler.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/blocks/cron/java/org/apache/cocoon/components/cron/QuartzJobScheduler.java
 (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/blocks/cron/java/org/apache/cocoon/components/cron/QuartzJobScheduler.java
 Mon Nov  8 05:16:55 2004
@@ -38,6 +38,8 @@
 import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.avalon.framework.service.Serviceable;
 import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.components.thread.RunnableManager;
+import org.apache.cocoon.components.thread.ThreadPool;
 import org.quartz.CronTrigger;
 import org.quartz.Job;
 import org.quartz.JobDataMap;
@@ -58,10 +60,6 @@
 import org.quartz.utils.DBConnectionManager;
 import org.quartz.utils.JNDIConnectionProvider;
 
-import EDU.oswego.cs.dl.util.concurrent.BoundedBuffer;
-import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
-import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
-
 /**
  * This component can either schedule jobs or directly execute one.
  *
@@ -75,19 +73,19 @@
                                            Serviceable, Configurable, 
Startable,
                                            Disposable, Contextualizable, 
Initializable {
 
-    /** ThreadPool policy RUN */
+    /** QuartzThreadPool policy RUN */
     private static final String POLICY_RUN = "RUN";
 
-    /** ThreadPool policy WAIT */
+    /** QuartzThreadPool policy WAIT */
     private static final String POLICY_WAIT = "WAIT";
 
-    /** ThreadPool policy ABORT */
+    /** QuartzThreadPool policy ABORT */
     private static final String POLICY_ABORT = "ABORT";
 
-    /** ThreadPool policy DISCARD */
+    /** QuartzThreadPool policy DISCARD */
     private static final String POLICY_DISCARD = "DISCARD";
 
-    /** ThreadPool policy DISCARD-OLDEST */
+    /** QuartzThreadPool policy DISCARD-OLDEST */
     private static final String POLICY_DISCARD_OLDEST = "DISCARDOLDEST";
 
 
@@ -135,7 +133,7 @@
     private Context context;
 
     /** The PooledExecutor instance */
-    private PooledExecutor executor;
+    private ThreadPool executor;
 
     /** The quartz scheduler */
     private Scheduler scheduler;
@@ -317,7 +315,7 @@
             // If cocoon reloads (or is it the container that reload us?)
             // we cannot create the same scheduler again
             final String runID = new Date().toString().replace(' ', '_');
-            final ThreadPool pool = 
createThreadPool(this.config.getChild("thread-pool"));
+            final QuartzThreadPool pool = 
createThreadPool(this.config.getChild("thread-pool"));
             final JobStore store = 
createJobStore(DEFAULT_QUARTZ_SCHEDULER_NAME, runID, 
this.config.getChild("store"));
             
DirectSchedulerFactory.getInstance().createScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME,
 runID, pool, store);
             // scheduler = 
DirectSchedulerFactory.getInstance().getScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME,
 runID);
@@ -592,85 +590,33 @@
     }
 
     /**
-     * Create a ThreadPool
+     * Create a QuartzThreadPool
      *
      * @param poolConfig Configuration element for the thread pool
      *
-     * @return ThreadPool
+     * @return QuartzThreadPool
      */
-    private ThreadPool createThreadPool(final Configuration poolConfig) {
+    private QuartzThreadPool createThreadPool(final Configuration poolConfig) 
+    throws ServiceException {
         final boolean useQueueing = 
poolConfig.getChild("use-queueing").getValueAsBoolean(false);
         final int queueSize = 
poolConfig.getChild("queue-size").getValueAsInteger(-1);
-
-        if (useQueueing) {
-            if (queueSize > 0) {
-                this.executor = new PooledExecutor(new 
BoundedBuffer(queueSize));
-            } else {
-                this.executor = new PooledExecutor(new LinkedQueue());
-            }
-        } else {
-            this.executor = new PooledExecutor();
-        }
-
         final int maxPoolSize = 
poolConfig.getChild("max-pool-size").getValueAsInteger(-1);
-
-        if (maxPoolSize > 0) {
-            this.executor.setMaximumPoolSize(maxPoolSize);
-        } else {
-            
this.executor.setMaximumPoolSize(PooledExecutor.DEFAULT_MAXIMUMPOOLSIZE);
-        }
-
         final int minPoolSize = 
poolConfig.getChild("min-pool-size").getValueAsInteger(-1);
-
-        if (minPoolSize > 0) {
-            this.executor.setMinimumPoolSize(minPoolSize);
-        } else {
-            
this.executor.setMinimumPoolSize(PooledExecutor.DEFAULT_MINIMUMPOOLSIZE);
-        }
-
         final int keepAliveTimeMs = 
poolConfig.getChild("keep-alive-time-ms").getValueAsInteger(-1);
-
-        if (keepAliveTimeMs > 0) {
-            this.executor.setKeepAliveTime(keepAliveTimeMs);
-        } else {
-            
this.executor.setKeepAliveTime(PooledExecutor.DEFAULT_KEEPALIVETIME);
-        }
-
         final String blockPolicy = 
poolConfig.getChild("block-policy").getValue(null);
-
-        if (blockPolicy != null) {
-            if (blockPolicy.equalsIgnoreCase(POLICY_ABORT)) {
-                this.executor.abortWhenBlocked();
-            } else if (blockPolicy.equalsIgnoreCase(POLICY_DISCARD)) {
-                this.executor.discardWhenBlocked();
-            } else if (blockPolicy.equalsIgnoreCase(POLICY_DISCARD_OLDEST)) {
-                this.executor.discardOldestWhenBlocked();
-            } else if (blockPolicy.equalsIgnoreCase(POLICY_RUN)) {
-                this.executor.runWhenBlocked();
-            } else if (blockPolicy.equalsIgnoreCase(POLICY_WAIT)) {
-                this.executor.waitWhenBlocked();
-            } else {
-                getLogger().warn("Unknown block-policy configuration '" + 
blockPolicy + "'. Should be one of '" +
-                                 POLICY_ABORT + "','" + POLICY_DISCARD + "','" 
+ POLICY_DISCARD_OLDEST + "','" +
-                                 POLICY_RUN + "','" + POLICY_WAIT + "'. Will 
use '" + POLICY_RUN + "'");
-            }
-        }
-
         m_shutdownGraceful = 
poolConfig.getChild("shutdown-graceful").getValueAsBoolean(true);
 
         final int shutdownWaitTimeMs = 
poolConfig.getChild("shutdown-wait-time-ms").getValueAsInteger(-1);
-        final ThreadPool pool = new ThreadPool(this.executor, 
shutdownWaitTimeMs);
-        pool.enableLogging(getLogger());
-
-        if (getLogger().isInfoEnabled()) {
-            getLogger().info("using a PooledExecutor as ThreadPool with 
queueing=" + useQueueing +
-                             (useQueueing ? (",queue-size=" + ((queueSize > 0) 
? ("" + queueSize) : "default")) : "") +
-                             ",max-pool-size=" + 
this.executor.getMaximumPoolSize() + ",min-pool-size=" +
-                             this.executor.getMinimumPoolSize() + 
",keep-alive-time-ms=" + this.executor.getKeepAliveTime() +
-                             ",block-policy='" + blockPolicy + 
"',shutdown-wait-time-ms=" +
-                             ((shutdownWaitTimeMs > 0) ? ("" + 
shutdownWaitTimeMs) : "default"));
-        }
-
+        final RunnableManager runnableManager = 
(RunnableManager)this.manager.lookup(RunnableManager.ROLE);
+        final QuartzThreadPool pool = new 
QuartzThreadPool(runnableManager.createPool(queueSize,
+                                                                               
       maxPoolSize,
+                                                                               
       minPoolSize,
+                                                                               
       Thread.NORM_PRIORITY,
+                                                                               
       false, // no daemon
+                                                                               
       keepAliveTimeMs,
+                                                                               
       blockPolicy,
+                                                                               
       m_shutdownGraceful,
+                                                                               
       shutdownWaitTimeMs));
         return pool;
     }
 
@@ -809,42 +755,38 @@
     }
     
     /**
-     * A ThreadPool for the Quartz Scheduler based on Doug Leas concurrency 
utilities PooledExecutor
+     * A QuartzThreadPool for the Quartz Scheduler based on Doug Leas 
concurrency utilities PooledExecutor
      *
      * @author <a href="mailto:[EMAIL PROTECTED]">Giacomo Pati</a>
      * @version CVS $Id$
      */
-    private static class ThreadPool extends AbstractLogEnabled implements 
org.quartz.spi.ThreadPool {
+    private static class QuartzThreadPool extends AbstractLogEnabled 
implements org.quartz.spi.ThreadPool {
         /** Our executor thread pool */
-        private PooledExecutor executor;
-
-        /** How long to wait for running jobs to terminate on disposition */
-        private int m_shutdownWaitTimeMs;
+        private ThreadPool executor;
 
         /**
          *
          */
-        public ThreadPool(final PooledExecutor executor, final int 
shutownWaitTimeMs) {
+        public QuartzThreadPool(final ThreadPool executor) {
             super();
             this.executor = executor;
-            m_shutdownWaitTimeMs = shutownWaitTimeMs;
         }
 
         /* (non-Javadoc)
-         * @see org.quartz.spi.ThreadPool#getPoolSize()
+         * @see org.quartz.spi.QuartzThreadPool#getPoolSize()
          */
         public int getPoolSize() {
             return this.executor.getMaximumPoolSize();
         }
 
         /* (non-Javadoc)
-         * @see org.quartz.spi.ThreadPool#initialize()
+         * @see org.quartz.spi.QuartzThreadPool#initialize()
          */
         public void initialize() {
         }
 
         /* (non-Javadoc)
-         * @see org.quartz.spi.ThreadPool#runInThread(java.lang.Runnable)
+         * @see org.quartz.spi.QuartzThreadPool#runInThread(java.lang.Runnable)
          */
         public boolean runInThread(final Runnable job) {
             try {
@@ -857,29 +799,10 @@
         }
 
         /* (non-Javadoc)
-         * @see org.quartz.spi.ThreadPool#shutdown(boolean)
+         * @see org.quartz.spi.QuartzThreadPool#shutdown(boolean)
          */
         public void shutdown(final boolean waitForJobsToComplete) {
-            if (waitForJobsToComplete) {
-                this.executor.shutdownAfterProcessingCurrentlyQueuedTasks();
-            } else {
-                this.executor.shutdownNow();
-            }
-
-            try {
-                if (m_shutdownWaitTimeMs > 0) {
-                    if 
(!this.executor.awaitTerminationAfterShutdown(m_shutdownWaitTimeMs)) {
-                        getLogger().warn("scheduled cron jobs are not 
terminating within " + m_shutdownWaitTimeMs +
-                                         "ms, Will shut them down by 
interruption");
-                        this.executor.interruptAll();
-                        this.executor.shutdownNow();
-                    }
-                }
-
-                this.executor.awaitTerminationAfterShutdown();
-            } catch (final InterruptedException ie) {
-                getLogger().error("cannot shutdown Executor", ie);
-            }
+            this.executor.shutdown();
         }
     }
 }

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/cron.js
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/cron.js        
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/cron.js        Mon Nov 
 8 05:16:55 2004
@@ -44,6 +44,7 @@
     var cronexpr = "";
     var intervalexpr = "";
     var atexpr = "";
+    var i = 0;
     while( ! done )
     {
         var jobnames = scheduler.getJobNames();

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/docs.xml
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/docs.xml       
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/cron/samples/docs.xml       Mon Nov 
 8 05:16:55 2004
@@ -26,10 +26,7 @@
                 <code>JobScheduler</code></link> 
                 is based on the <link href="http://quartz.sf.net";>Quartz</link>
                 job scheduling project and the 
-                <link 
href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/PooledExecutor.html";>
-                <code>PooledExecutor</code></link> of 
-                <link 
href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html";>
-                Doug Leas Concurrency Package</link> as a ThreadPool 
implementation for the Quartz Scheduler.
+                <code>RunnableManager</code></link> as a ThreadPool 
implementation for the Quartz Scheduler.
             </p>
             
             <p style="background-color: yellow">
@@ -113,9 +110,8 @@
 &lt;/thread-pool&gt;
                 </source>
                 <p>
-                    As mentioned in the beginning, more information about the 
thread pool details of the base
-                    <link 
href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/PooledExecutor.html";>
-                    <code>PooledExecutor</code></link> class can be found 
there.
+                    As mentioned in the beginning, more information about the 
thread pool details base on the
+                    <code>RunnableManager#createPool(...)</code></link> method 
can be found there.
                 </p>
             </section>
             

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/conf/hsqldb.xconf
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/conf/hsqldb.xconf    
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/conf/hsqldb.xconf    Mon Nov 
 8 05:16:55 2004
@@ -30,6 +30,7 @@
      <parameter name="port" value="9002"/>
      <parameter name="silent" value="true"/>
      <parameter name="trace" value="false"/>
+     <parameter name="thread-pool-name" value="daemon"/>
   </hsqldb-server>
 
 </xconf>

Modified: 
cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/java/org/apache/cocoon/components/hsqldb/ServerImpl.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/java/org/apache/cocoon/components/hsqldb/ServerImpl.java
     (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/blocks/hsqldb/java/org/apache/cocoon/components/hsqldb/ServerImpl.java
     Mon Nov  8 05:16:55 2004
@@ -23,6 +23,7 @@
 import java.sql.SQLException;
 import java.sql.Statement;
 
+import org.apache.avalon.framework.CascadingRuntimeException;
 import org.apache.avalon.framework.activity.Startable;
 import org.apache.avalon.framework.context.Context;
 import org.apache.avalon.framework.context.ContextException;
@@ -30,16 +31,21 @@
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
 import org.apache.avalon.framework.parameters.Parameterizable;
 import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
 import org.apache.avalon.framework.thread.ThreadSafe;
 
 import org.apache.cocoon.Constants;
+import org.apache.cocoon.components.thread.RunnableManager;
+import org.apache.cocoon.components.thread.ThreadPool;
 
 /**
  * This class runs an instance of HSQLDB Server.
  *
  * @author <a href="mailto:[EMAIL PROTECTED]">Davanum Srinivas</a>
  * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a>
- * @version CVS $Id: ServerImpl.java,v 1.3 2004/03/05 13:01:56 bdelacretaz Exp 
$
+ * @version CVS $Id$
  */
 public class ServerImpl extends AbstractLogEnabled
     implements Server,
@@ -47,6 +53,7 @@
                Contextualizable,
                ThreadSafe,
                Runnable,
+               Serviceable,
                Startable {
 
     /** Port which HSQLDB server will listen to */
@@ -55,9 +62,15 @@
     /** Arguments for running the server */
     private String arguments[] = new String[10];
 
+    /** The threadpool name to be used for daemon thread */
+    private String m_daemonThreadPoolName = "daemon";
+
     /** Check if the server has already been started */
     private boolean started = false;
 
+    /** The [EMAIL PROTECTED] ServiceManager} instance */
+    private ServiceManager m_serviceManager;
+    
     /**
      * Initialize the ServerImpl.
      * A few options can be used :
@@ -83,6 +96,7 @@
                     + ", silent: " + arguments[3]
                     + ", trace: " +arguments[5]);
         }
+        m_daemonThreadPoolName = params.getParameter( "thread-pool-name", 
m_daemonThreadPoolName );
     }
 
     /** Contextualize this class */
@@ -108,6 +122,15 @@
             getLogger().error("IOException - Could not get database directory 
", e);
         }
     }
+    /**
+     * @param serviceManager The <@link ServiceManager} instance
+     * @throws ServiceException In case we cannot find a service needed
+     */
+    public void service( ServiceManager serviceManager )
+    throws ServiceException
+    {
+        m_serviceManager = serviceManager;
+    }
 
     /** Start the server */
     public void start() {
@@ -118,12 +141,24 @@
                 getLogger().info("HSQLDB backup file has been deleted.");
             }
 
-            Thread server = new Thread(this);
+            RunnableManager runnableManager = null;
+            try
+            {
             this.getLogger().debug("Intializing hsqldb server thread");
-            server.setPriority(Thread.currentThread().getPriority());
-            server.setDaemon(true);
-            server.setName("hsqldb server");
-            server.start();
+                runnableManager = 
(RunnableManager)m_serviceManager.lookup(RunnableManager.ROLE);
+                runnableManager.execute( m_daemonThreadPoolName, this );
+            }
+            catch( final ServiceException se )
+            {
+                throw new CascadingRuntimeException( "Cannot get 
RunnableManager", se );
+            }
+            finally
+            {
+                if( null != runnableManager )
+                {
+                    m_serviceManager.release( runnableManager );
+                }
+            }
         }
     }
 

Modified: 
cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/coplet/adapter/impl/AbstractCopletAdapter.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/coplet/adapter/impl/AbstractCopletAdapter.java
 (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/coplet/adapter/impl/AbstractCopletAdapter.java
 Mon Nov  8 05:16:55 2004
@@ -23,6 +23,7 @@
 import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.avalon.framework.service.Serviceable;
 import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.components.thread.RunnableManager;
 import org.apache.cocoon.portal.coplet.CopletData;
 import org.apache.cocoon.portal.coplet.CopletInstanceData;
 import org.apache.cocoon.portal.coplet.adapter.CopletAdapter;
@@ -30,6 +31,7 @@
 import org.apache.cocoon.xml.XMLUtils;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
+import EDU.oswego.cs.dl.util.concurrent.CountDown;
 
 /**
  * This is the abstract base adapter to use pipelines as coplets
@@ -60,7 +62,7 @@
  * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a>
  * @author <a href="mailto:[EMAIL PROTECTED]">Volker Schmitt</a>
  * 
- * @version CVS $Id: AbstractCopletAdapter.java,v 1.11 2004/04/25 20:09:34 
haul Exp $
+ * @version CVS $Id$
  */
 public abstract class AbstractCopletAdapter 
     extends AbstractLogEnabled
@@ -115,14 +117,13 @@
                 if ( timeout != null ) {
                     final int milli = timeout.intValue() * 1000;
                     LoaderThread loader = new LoaderThread(this, coplet, 
buffer);
-                    Thread thread = new Thread(loader);
-                    thread.start();
+                    final RunnableManager runnableManager = 
(RunnableManager)this.manager.lookup( RunnableManager.ROLE );
+                    runnableManager.execute( loader );
+                    this.manager.release( runnableManager );
                     try {
-                        thread.join(milli);
+                        read = loader.join( milli );
                     } catch (InterruptedException ignore) {
-                    }
-                    if ( loader.finished ) {
-                        read = true;
+                        // ignored
                     }
                 } else {
                     this.streamContent( coplet, buffer );
@@ -202,10 +203,10 @@
 
 final class LoaderThread implements Runnable {
     
-    private  AbstractCopletAdapter adapter;
-    private  ContentHandler        handler;
-    private  CopletInstanceData    coplet;
-    boolean  finished;
+    private final AbstractCopletAdapter adapter;
+    private final ContentHandler        handler;
+    private final CopletInstanceData    coplet;
+    private final CountDown             finished;
     Exception exception;
 
     public LoaderThread(AbstractCopletAdapter adapter, 
@@ -214,6 +215,7 @@
         this.adapter = adapter;
         this.coplet  = coplet;
         this.handler = handler;
+        this.finished = new CountDown( 1 );
     }
     
     public void run() {
@@ -222,8 +224,14 @@
         } catch (Exception local) {
             this.exception = local;
         } finally {
-            this.finished = true;
+            this.finished.release();
         }
+    }
+    
+    boolean join( final long milis )
+    throws InterruptedException 
+    {
+        return this.finished.attempt( milis );
     }
     
 }

Modified: 
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java
    (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java
    Mon Nov  8 05:16:55 2004
@@ -15,6 +15,7 @@
  */
 package org.apache.cocoon.transformation;
 
+import org.apache.avalon.framework.CascadingRuntimeException;
 import org.apache.avalon.framework.configuration.Configurable;
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
@@ -27,6 +28,7 @@
 import org.apache.cocoon.caching.CacheableProcessingComponent;
 import org.apache.cocoon.components.source.SourceUtil;
 import org.apache.cocoon.components.source.impl.MultiSourceValidity;
+import org.apache.cocoon.components.thread.RunnableManager;
 import org.apache.cocoon.environment.SourceResolver;
 import org.apache.cocoon.util.NetUtils;
 import org.apache.cocoon.xml.EmbeddedXMLPipe;
@@ -618,13 +620,15 @@
         public IncludeBuffer(Source source) {
             this.source = source;
 
-            // FIXME Need thread pool component. Based on 
EDU.oswego.cs.dl.util.concurrent.PooledExecutor.
-            //       See also 
org.apache.cocoon.components.cron.QuartzJobScheduler.ThreadPool
             try {
-                Thread t = new Thread(this);
-                t.setName("IncludeSource#" + source.getURI());
-                t.setDaemon(true);
-                t.start();
+                final RunnableManager runnableManager = 
(RunnableManager)m_manager.lookup( RunnableManager.ROLE );
+                runnableManager.execute( "daemon", this ); // XXX: GP: Do we 
really need daemon threads here ?
+                m_manager.release( runnableManager );
+            } catch (final ServiceException e) {
+                // In case we failed to spawn a thread
+                this.e = new SAXException(e);
+                m_resolver.release(source);
+                throw new CascadingRuntimeException( e.getMessage(), e );
             } catch (RuntimeException e) {
                 // In case we failed to spawn a thread
                 this.e = new SAXException(e);

Modified: 
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/JavaLanguage.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/JavaLanguage.java
   (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/JavaLanguage.java
   Mon Nov  8 05:16:55 2004
@@ -43,7 +43,7 @@
  * The Java programming language processor
  *
  * @author <a href="mailto:[EMAIL PROTECTED]">Ricardo Rocha</a>
- * @version CVS $Id: JavaLanguage.java,v 1.1 2004/03/10 12:58:07 stephan Exp $
+ * @version CVS $Id$
  */
 public class JavaLanguage extends CompiledProgrammingLanguage
         implements Initializable, ThreadSafe, Serviceable, Disposable {
@@ -174,6 +174,9 @@
             if (compiler instanceof LogEnabled) {
                 ((LogEnabled)compiler).enableLogging(getLogger());
             }
+            if (compiler instanceof Serviceable) {
+                ((Serviceable)compiler).service(this.manager);
+            }
 
             int pos = name.lastIndexOf(File.separatorChar);
             String filename = name.substring(pos + 1);
@@ -213,6 +216,9 @@
         } catch (IOException e) {
             getLogger().warn("Error during compilation", e);
             throw new LanguageException("Error during compilation: " + 
e.getMessage());
+        } catch (ServiceException e) {
+            getLogger().warn("Could not initialize the compiler", e);
+            throw new LanguageException("Could not initialize the compiler: " 
+ e.getMessage());
         }
     }
 

Modified: 
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/Jikes.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/Jikes.java
  (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/blocks/xsp/java/org/apache/cocoon/components/language/programming/java/Jikes.java
  Mon Nov  8 05:16:55 2004
@@ -25,31 +25,40 @@
 import java.util.List;
 import java.util.StringTokenizer;
 
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
 import org.apache.cocoon.components.language.programming.CompilerError;
+import org.apache.cocoon.components.thread.RunnableManager;
+import EDU.oswego.cs.dl.util.concurrent.CountDown;
 
 /**
  * This class wraps IBM's <i>Jikes</i> Java compiler
  * NOTE: inspired by the Apache Jasper implementation.
  * @author <a href="mailto:[EMAIL PROTECTED]">Stefano Mazzocchi</a>
- * @version CVS $Id: Jikes.java,v 1.1 2004/03/10 12:58:07 stephan Exp $
+ * @version CVS $Id$
  * @since 2.0
  */
 
-public class Jikes extends AbstractJavaCompiler {
+public class Jikes extends AbstractJavaCompiler implements Serviceable {
 
     static final int OUTPUT_BUFFER_SIZE = 1024;
     static final int BUFFER_SIZE = 512;
 
-    private class StreamPumper extends Thread {
+    private ServiceManager m_serviceManager;
+    
+    private class StreamPumper implements Runnable {
 
         private BufferedInputStream stream;
         private boolean endOfStream = false;
         private int SLEEP_TIME = 5;
         private OutputStream out;
+        private CountDown m_done;
 
-        public StreamPumper(BufferedInputStream is, OutputStream out) {
+        public StreamPumper(BufferedInputStream is, OutputStream out, 
CountDown done) {
             this.stream = is;
             this.out = out;
+            m_done = done;
         }
 
         public void pumpStream() throws IOException {
@@ -69,12 +78,22 @@
             try {
                 while (!endOfStream) {
                     pumpStream();
-                    sleep(SLEEP_TIME);
+                    Thread.sleep(SLEEP_TIME);
                 }
             } catch (Exception e) {
                // getLogger().warn("Jikes.run()", e);
             }
+            m_done.release(); // signal 'we are finished'
         }
+        }
+
+    /**
+     * Set the [EMAIL PROTECTED] ServiceManager}
+     */
+    public void service( ServiceManager serviceManager )
+    throws ServiceException
+    {
+        m_serviceManager = serviceManager;
     }
 
     /**
@@ -128,15 +147,26 @@
 
             BufferedInputStream compilerErr = new 
BufferedInputStream(p.getErrorStream());
 
-            StreamPumper errPumper = new StreamPumper(compilerErr, tmpErr);
+            RunnableManager runnableManager = null;
+            try
+            {
+                runnableManager = (RunnableManager)m_serviceManager.lookup( 
RunnableManager.ROLE );
+            }
+            catch( final ServiceException se )
+            {
+                getLogger().error( "Cannot get RunnableManager", se );
+                throw new IOException( "Cannot get RunnableManager" );
+            }
 
-            errPumper.start();
+            final CountDown done = new CountDown( 1 );
+            StreamPumper errPumper = new StreamPumper(compilerErr, tmpErr, 
done);
+            runnableManager.execute( errPumper );
+            m_serviceManager.release( runnableManager );
 
             p.waitFor();
             exitValue = p.exitValue();
 
-            // Wait until the complete error stream has been read
-            errPumper.join();
+            done.acquire(); // Wait for StreadmPumper to finish
             compilerErr.close();
 
             p.destroy();

Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles        
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles        
Mon Nov  8 05:16:55 2004
@@ -172,4 +172,9 @@
         shorthand="request-data-store"
         
default-class="org.apache.cocoon.components.persistence.RequestDataStoreImpl"/>
 
+  <!-- Running commands (Runnable) in background -->
+  <role name="org.apache.cocoon.components.thread.RunnableManager"
+        shorthand="runnable-manager"
+        
default-class="org.apache.cocoon.components.thread.DefaultRunnableManager"/>
+
 </role-list>

Modified: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
       (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/flow/ContinuationsManagerImpl.java
       Mon Nov  8 05:16:55 2004
@@ -22,7 +22,11 @@
 import org.apache.avalon.framework.context.ContextException;
 import org.apache.avalon.framework.context.Contextualizable;
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
 import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.components.thread.RunnableManager;
 
 import org.apache.excalibur.event.Queue;
 import org.apache.excalibur.event.Sink;
@@ -55,7 +59,7 @@
 public class ContinuationsManagerImpl
         extends AbstractLogEnabled
         implements ContinuationsManager, Component, Configurable,
-                   ThreadSafe, Contextualizable, Instrumentable {
+                   ThreadSafe, Instrumentable, Serviceable {
 
     static final int CONTINUATION_ID_LENGTH = 20;
     static final String EXPIRE_CONTINUATIONS = "expire-continuations";
@@ -66,8 +70,6 @@
     protected SecureRandom random;
     protected byte[] bytes;
 
-    protected Sink m_commandSink;
-
     /**
      * How long does a continuation exist in memory since the last
      * access? The time is in miliseconds, and the default is 1 hour.
@@ -101,6 +103,7 @@
     private CounterInstrument continuationsCreated;
     private CounterInstrument continuationsInvalidated;
 
+    private ServiceManager serviceManager;
 
     public ContinuationsManagerImpl() throws Exception {
         try {
@@ -122,17 +125,25 @@
     /**
      * Get the command sink so that we can be notified of changes
      */
-    public void contextualize(Context context) throws ContextException {
-        m_commandSink = (Sink) context.get(Queue.ROLE);
+    public void service(final ServiceManager manager) throws ServiceException {
+        this.serviceManager = manager;
     }
 
     public void configure(Configuration config) {
         this.defaultTimeToLive = config.getAttributeAsInteger("time-to-live", 
(3600 * 1000));
 
         final Configuration expireConf = config.getChild("expirations-check");
+        final long initialDelay = expireConf.getChild("offset", 
true).getValueAsLong(180000);
+        final long interval = expireConf.getChild("period", 
true).getValueAsLong(180000);
         try {
-            final ContinuationInterrupt interrupt = new 
ContinuationInterrupt(expireConf);
-            this.m_commandSink.enqueue(interrupt);
+            final RunnableManager runnableManager = 
(RunnableManager)serviceManager.lookup(RunnableManager.ROLE);
+            runnableManager.execute( new Runnable() {
+                public void run()
+                {
+                    expireContinuations();
+                }
+            }, initialDelay, interval);
+            serviceManager.release(runnableManager);
         } catch (Exception e) {
             getLogger().warn("Could not enqueue continuations expiration task. 
" +
                              "Continuations will not automatically expire.", 
e);
@@ -363,7 +374,7 @@
     /**
      * Remove all continuations which have already expired.
      */
-    private void expireContinuations() {
+    protected void expireContinuations() {
         long now = 0;
         if (getLogger().isDebugEnabled()) {
             now = System.currentTimeMillis();
@@ -395,51 +406,6 @@
             displayAllContinuations();
             displayExpireSet();
             */
-        }
-    }
-
-
-    final class ContinuationInterrupt implements RepeatedCommand {
-        private final long m_interval;
-        private final long m_initialDelay;
-
-        /**
-         * @param expireConf
-         */
-        public ContinuationInterrupt(Configuration expireConf) {
-            // only periodic time triggers are supported
-            m_initialDelay =
-                    expireConf.getChild("offset", true).getValueAsLong(100);
-            m_interval =
-                    expireConf.getChild("period", true).getValueAsLong(100);
-        }
-
-        /**
-         * Repeat forever
-         */
-        public int getNumberOfRepeats() {
-            return -1;
-        }
-
-        /**
-         * Get the number of millis to wait between invocations
-         */
-        public long getRepeatInterval() {
-            return m_interval;
-        }
-
-        /**
-         * Get the number of millis to wait for the first invocation
-         */
-        public long getDelayInterval() {
-            return m_initialDelay;
-        }
-
-        /**
-         * expire any continuations that need expiring.
-         */
-        public void execute() throws Exception {
-            expireContinuations();
         }
     }
 }

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/BoundedQueue.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/BoundedQueue.java
 Mon Nov  8 05:16:55 2004
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * Efficient array-based bounded buffer class. Adapted from CPJ, chapter 8,
+ * which describes design.
+ * 
+ * <p>
+ * [<a
+ * 
href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html";>
+ * Introduction to this package. </a>]
+ * </p>
+ * 
+ * <p></p>
+ */
+public class BoundedQueue
+    extends EDU.oswego.cs.dl.util.concurrent.BoundedBuffer
+    implements Queue
+{
+    //~ Constructors 
-----------------------------------------------------------
+
+    /**
+     * Create a buffer with the current default capacity
+     */
+    public BoundedQueue(  )
+    {
+        super(  );
+    }
+
+    /**
+     * Create a BoundedQueue with the given capacity.
+     *
+     * @param capacity The capacity
+     *
+     * @exception IllegalArgumentException if capacity less or equal to zero
+     */
+    public BoundedQueue( int capacity )
+        throws IllegalArgumentException
+    {
+        super( capacity );
+    }
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return
+     */
+    public int getQueueSize(  )
+    {
+        return usedSlots_;
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ChannelWrapper.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ChannelWrapper.java
       Mon Nov  8 05:16:55 2004
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+import EDU.oswego.cs.dl.util.concurrent.Channel;
+
+
+/**
+ * Wrapper around a Channel implementation for constructor convenience
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version $Id: ChannelWrapper.java 56702 2004-11-05 22:52:05Z giacomo $
+ */
+public class ChannelWrapper
+    implements Channel
+{
+    //~ Instance fields 
--------------------------------------------------------
+
+    /** The wrapped Channel */
+    private Channel m_channel;
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param channel DOCUMENT ME!
+     */
+    public void setChannel( final Channel channel )
+    {
+        m_channel = channel;
+    }
+
+    /**
+     * @see EDU.oswego.cs.dl.util.concurrent.Puttable#offer(java.lang.Object,
+     *      long)
+     */
+    public boolean offer( final Object obj,
+                          final long timeout )
+        throws InterruptedException
+    {
+        return m_channel.offer( obj, timeout );
+    }
+
+    /**
+     * @see EDU.oswego.cs.dl.util.concurrent.Channel#peek()
+     */
+    public Object peek(  )
+    {
+        return m_channel.peek(  );
+    }
+
+    /**
+     * @see EDU.oswego.cs.dl.util.concurrent.Takable#poll(long)
+     */
+    public Object poll( final long timeout )
+        throws InterruptedException
+    {
+        return m_channel.poll( timeout );
+    }
+
+    /**
+     * @see EDU.oswego.cs.dl.util.concurrent.Puttable#put(java.lang.Object)
+     */
+    public void put( final Object obj )
+        throws InterruptedException
+    {
+        m_channel.put( obj );
+    }
+
+    /**
+     * @see EDU.oswego.cs.dl.util.concurrent.Takable#take()
+     */
+    public Object take(  )
+        throws InterruptedException
+    {
+        return m_channel.take(  );
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultRunnableManager.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultRunnableManager.java
       Mon Nov  8 05:16:55 2004
@@ -0,0 +1,859 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Startable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+/**
+ * The DefaultRunnableManager implements the [EMAIL PROTECTED] 
RunnableManager} interface
+ * and is responsible to create [EMAIL PROTECTED] ThreadPool}s and run [EMAIL 
PROTECTED] Runnable}s
+ * in them as background commands.
+ * 
+ * <p>
+ * The configuration of the <code>DefaultRunnableManager</code>:
+ * <pre>
+ *   
&lt;thread-factory&gt;org.apache.cocoon.components.thread.DefaultThreadFactory&lt;/thread-factory&gt;
+ *   &lt;thread-pools&gt;
+ *     &lt;thread-pool&gt;  
+ *       &lt;name&gt;default&lt;/name&gt;
+ *       &lt;priority&gt;NORM&lt;/priority&gt;
+ *       &lt;daemon&gt;false&lt;/daemon&gt;
+ *       &lt;queue-size&gt;-1&lt;/queue-size&gt;
+ *       &lt;max-pool-size&gt;-1&lt;/max-pool-size&gt;
+ *       &lt;min-pool-size&gt;2&lt;/min-pool-size&gt;
+ *       &lt;keep-alive-time-ms&gt;20000&lt;/keep-alive-time-ms&gt;
+ *       &lt;block-policy&gt;RUN&lt;/block-policy&gt;
+ *       &lt;shutdown-graceful&gt;false&lt;/shutdown-graceful&gt;
+ *       &lt;shutdown-wait-time-ms&gt;-1&lt;/shutdown-wait-time-ms&gt;
+ *     &lt;/thread-pool&gt; 
+ *   &lt;/thread-pools&gt;
+ * </pre>
+ * </p>
+ * 
+ * <p>
+ * Have a look at
+ * 
http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/PooledExecutor.html,
+ * [EMAIL PROTECTED] EDU.oswego.cs.dl.util.concurrent.PooledExecutor} or the 
cocoon.xconf
+ * file for more information.
+ * </p>
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version $Id: DefaultRunnableManager.java 56848 2004-11-07 14:09:23Z 
giacomo $
+ */
+public class DefaultRunnableManager
+    extends AbstractLogEnabled
+    implements RunnableManager, Configurable, Disposable, Runnable, Startable,
+                   ThreadSafe
+{
+    //~ Static fields/initializers 
---------------------------------------------
+
+    /** The default [EMAIL PROTECTED] ThreadFactory} */
+    public static final String DEFAULT_THREAD_FACTORY =
+        DefaultThreadFactory.class.getName(  );
+
+    /** The default queue size */
+    public static final int DEFAULT_QUEUE_SIZE = -1;
+
+    /** The default maximum pool size */
+    public static final int DEFAULT_MAX_POOL_SIZE = 5;
+
+    /** The default minimum pool size */
+    public static final int DEFAULT_MIN_POOL_SIZE = 5;
+
+    /** The default thread priority */
+    public static final String DEFAULT_THREAD_PRIORITY = "NORM";
+
+    /** The default daemon mode */
+    public static final boolean DEFAULT_DAEMON_MODE = false;
+
+    /** The default keep alive time */
+    public static final long DEFAULT_KEEP_ALIVE_TIME = 60000L;
+
+    /** The default way to shutdown gracefully */
+    public static final boolean DEFAULT_SHUTDOWN_GRACEFUL = false;
+
+    /** The default shutdown waittime time */
+    public static final int DEFAULT_SHUTDOWN_WAIT_TIME = -1;
+
+    /** The default shutdown waittime time */
+    public static final String DEFAULT_THREADPOOL_NAME = "default";
+
+    //~ Instance fields 
--------------------------------------------------------
+
+    /**
+     * Sorted set of <code>ExecutionInfo</code> instances, based on their next
+     * execution time.
+     */
+    protected SortedSet m_commandStack = new TreeSet(  );
+
+    /** The managed thread pools */
+    final Map m_pools = new HashMap(  );
+
+    /** The configured default ThreadFactory class instance */
+    private Class m_defaultThreadFactoryClass;
+
+    /** Keep us running? */
+    private boolean m_keepRunning = false;
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * @see 
org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+     */
+    public void configure( final Configuration config )
+        throws ConfigurationException
+    {
+        final String defaultThreadFactoryName =
+            config.getChild( "thread-factory" ).getValue( 
DEFAULT_THREAD_FACTORY );
+
+        try
+        {
+            m_defaultThreadFactoryClass =
+                Thread.currentThread(  ).getContextClassLoader(  ).loadClass( 
defaultThreadFactoryName );
+        }
+        catch( final Exception ex )
+        {
+            throw new ConfigurationException( "Cannot create instance of 
default thread factory " +
+                                              defaultThreadFactoryName, ex );
+        }
+
+        final Configuration [] threadpools =
+            config.getChild( "thread-pools" ).getChildren( "thread-pool" );
+
+        for( int i = 0; i < threadpools.length; i++ )
+        {
+            final DefaultThreadPool pool = configThreadPool( threadpools[ i ] 
);
+        }
+
+        // Check if a "default" pool has been created
+        final ThreadPool defaultThreadPool =
+            (ThreadPool)m_pools.get( DEFAULT_THREADPOOL_NAME );
+
+        if( null == defaultThreadPool )
+        {
+            createPool( DEFAULT_THREADPOOL_NAME, DEFAULT_QUEUE_SIZE,
+                        DEFAULT_MAX_POOL_SIZE, DEFAULT_MIN_POOL_SIZE,
+                        getPriority( DEFAULT_THREAD_PRIORITY ),
+                        DEFAULT_DAEMON_MODE, DEFAULT_KEEP_ALIVE_TIME,
+                        DefaultThreadPool.POLICY_DEFAULT,
+                        DEFAULT_SHUTDOWN_GRACEFUL, DEFAULT_SHUTDOWN_WAIT_TIME 
);
+        }
+    }
+
+    /**
+     * Create a shared ThreadPool
+     *
+     * @param name The name of the thread pool
+     * @param queueSize The size of the queue
+     * @param maxPoolSize The maximum number of threads
+     * @param minPoolSize The maximum number of threads
+     * @param priority The priority of threads created by this pool. This is
+     *        one of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *        Thread#NORM_PRIORITY}, or [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     * @param isDaemon Whether or not thread from the pool should run in daemon
+     *        mode
+     * @param keepAliveTime How long should a thread be alive for new work to
+     *        be done before it is GCed
+     * @param blockPolicy What's the blocking policy is resources are exhausted
+     * @param shutdownGraceful Should we wait for the queue to finish all
+     *        pending commands?
+     * @param shutdownWaitTime After what time a normal shutdown should take
+     *        into account if a graceful shutdown has not come to an end
+     *
+     * @throws IllegalArgumentException If the pool already exists
+     */
+    public void createPool( final String name,
+                            final int queueSize,
+                            final int maxPoolSize,
+                            final int minPoolSize,
+                            final int priority,
+                            final boolean isDaemon,
+                            final long keepAliveTime,
+                            final String blockPolicy,
+                            final boolean shutdownGraceful,
+                            final int shutdownWaitTime )
+    {
+        if( null != m_pools.get( name ) )
+        {
+            throw new IllegalArgumentException( "ThreadPool \"" + name +
+                                                "\" already exists" );
+        }
+
+        createPool( new DefaultThreadPool(  ), name, queueSize, maxPoolSize,
+                    minPoolSize, priority, isDaemon, keepAliveTime,
+                    blockPolicy, shutdownGraceful, shutdownWaitTime );
+    }
+
+    /**
+     * Create a private ThreadPool
+     *
+     * @param queueSize The size of the queue
+     * @param maxPoolSize The maximum number of threads
+     * @param minPoolSize The maximum number of threads
+     * @param priority The priority of threads created by this pool. This is
+     *        one of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *        Thread#NORM_PRIORITY}, or [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     * @param isDaemon Whether or not thread from the pool should run in daemon
+     *        mode
+     * @param keepAliveTime How long should a thread be alive for new work to
+     *        be done before it is GCed
+     * @param blockPolicy What's the blocking policy is resources are exhausted
+     * @param shutdownGraceful Should we wait for the queue to finish all
+     *        pending commands?
+     * @param shutdownWaitTime After what time a normal shutdown should take
+     *        into account if a graceful shutdown has not come to an end
+     *
+     * @return A newly created <code>ThreadPool</code>
+     */
+    public ThreadPool createPool( final int queueSize,
+                                  final int maxPoolSize,
+                                  final int minPoolSize,
+                                  final int priority,
+                                  final boolean isDaemon,
+                                  final long keepAliveTime,
+                                  final String blockPolicy,
+                                  final boolean shutdownGraceful,
+                                  final int shutdownWaitTime )
+    {
+        final DefaultThreadPool pool = new DefaultThreadPool(  );
+        final String name = "anon-" + pool.hashCode(  );
+
+        return createPool( pool, name, queueSize, maxPoolSize, minPoolSize,
+                           priority, isDaemon, keepAliveTime, blockPolicy,
+                           shutdownGraceful, shutdownWaitTime );
+    }
+
+    /**
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose(  )
+    {
+        if( getLogger(  ).isDebugEnabled(  ) )
+        {
+            getLogger(  ).debug( "Disposing all thread pools" );
+        }
+
+        for( final Iterator i = m_pools.keySet(  ).iterator(  ); i.hasNext(  
); )
+        {
+            final String poolName = (String)i.next(  );
+            final DefaultThreadPool pool =
+                (DefaultThreadPool)m_pools.get( poolName );
+
+            if( getLogger(  ).isDebugEnabled(  ) )
+            {
+                getLogger(  ).debug( "Disposing thread pool " +
+                                     pool.getName(  ) );
+            }
+
+            pool.shutdown(  );
+
+            if( getLogger(  ).isDebugEnabled(  ) )
+            {
+                getLogger(  ).debug( "Thread pool " + pool.getName(  ) +
+                                     " disposed" );
+            }
+        }
+
+        try
+        {
+            m_pools.clear(  );
+        }
+        catch( final Throwable t )
+        {
+            getLogger(  ).error( "Cannot dispose", t );
+        }
+    }
+
+    /**
+     * Run a [EMAIL PROTECTED] Runnable} in the background using a [EMAIL 
PROTECTED] ThreadPool}
+     *
+     * @param threadPoolName The thread pool name to be used
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     * @param delay the delay befor first run
+     * @param interval The interval for repeated runs
+     *
+     * @throws IllegalArgumentException DOCUMENT ME!
+     */
+    public void execute( final String threadPoolName,
+                         final Runnable command,
+                         final long delay,
+                         long interval )
+    {
+        if( delay < 0 )
+        {
+            throw new IllegalArgumentException( "delay < 0" );
+        }
+
+        if( interval < 0 )
+        {
+            throw new IllegalArgumentException( "interval < 0" );
+        }
+
+        ThreadPool pool = (ThreadPool)m_pools.get( threadPoolName );
+
+        if( null == pool )
+        {
+            getLogger(  ).warn( "ThreadPool \"" + threadPoolName +
+                                "\" is not known. Will use ThreadPool \"" +
+                                DEFAULT_THREADPOOL_NAME + "\"" );
+            pool = (ThreadPool)m_pools.get( DEFAULT_THREADPOOL_NAME );
+        }
+
+        if( getLogger(  ).isDebugEnabled(  ) )
+        {
+            getLogger(  ).debug( "Command entered: " + command.toString(  ) +
+                                 ",pool=" + pool.getName(  ) + ",delay=" +
+                                 delay + ",interval=" + interval );
+        }
+
+        new ExecutionInfo( pool, command, delay, interval, getLogger(  ) );
+    }
+
+    /**
+     * Run a [EMAIL PROTECTED] Runnable} in the background using a [EMAIL 
PROTECTED] ThreadPool}
+     *
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     * @param delay the delay befor first run
+     * @param interval The interval for repeated runs
+     */
+    public void execute( final Runnable command,
+                         final long delay,
+                         final long interval )
+    {
+        execute( DEFAULT_THREADPOOL_NAME, command, delay, interval );
+    }
+
+    /**
+     * Run a [EMAIL PROTECTED] Runnable} in the background using a [EMAIL 
PROTECTED] ThreadPool}
+     *
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     * @param delay the delay befor first run
+     */
+    public void execute( final Runnable command,
+                         final long delay )
+    {
+        execute( DEFAULT_THREADPOOL_NAME, command, delay, 0 );
+    }
+
+    /**
+     * Run a [EMAIL PROTECTED] Runnable} in the background using a [EMAIL 
PROTECTED] ThreadPool}
+     *
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     */
+    public void execute( final Runnable command )
+    {
+        execute( DEFAULT_THREADPOOL_NAME, command, 0, 0 );
+    }
+
+    /**
+     * Run a [EMAIL PROTECTED] Runnable} in the background using a [EMAIL 
PROTECTED] ThreadPool}
+     *
+     * @param threadPoolName The thread pool name to be used
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     * @param delay the delay befor first run
+     */
+    public void execute( final String threadPoolName,
+                         final Runnable command,
+                         final long delay )
+    {
+        execute( threadPoolName, command, delay, 0 );
+    }
+
+    /**
+     * Run a [EMAIL PROTECTED] Runnable} in the background using a [EMAIL 
PROTECTED] ThreadPool}
+     *
+     * @param threadPoolName The thread pool name to be used
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     */
+    public void execute( final String threadPoolName,
+                         final Runnable command )
+    {
+        execute( threadPoolName, command, 0, 0 );
+    }
+
+    /**
+     * Remove a <code>Runnable</code> from the command stack
+     *
+     * @param command The <code>Runnable</code> to be removed
+     */
+    public void remove( Runnable command )
+    {
+        synchronized( m_commandStack )
+        {
+            for( final Iterator i = m_commandStack.iterator(  ); i.hasNext(  
); )
+            {
+                final ExecutionInfo info = (ExecutionInfo)i.next(  );
+
+                if( info.m_command == command )
+                {
+                    i.remove(  );
+                    m_commandStack.notifyAll(  );
+
+                    return;
+                }
+            }
+        }
+
+        getLogger(  ).warn( "Could not find command " + command +
+                            " for removal" );
+    }
+
+    /**
+     * The heart of the command manager
+     */
+    public void run(  )
+    {
+        if( getLogger(  ).isDebugEnabled(  ) )
+        {
+            getLogger(  ).debug( "Entering loop" );
+        }
+
+        while( m_keepRunning )
+        {
+            synchronized( m_commandStack )
+            {
+                try
+                {
+                    if( m_commandStack.size(  ) > 0 )
+                    {
+                        final ExecutionInfo info =
+                            (ExecutionInfo)m_commandStack.first(  );
+                        final long delay =
+                            info.m_nextRun - System.currentTimeMillis(  );
+
+                        if( delay > 0 )
+                        {
+                            m_commandStack.wait( delay );
+                        }
+                    }
+                    else
+                    {
+                        if( getLogger(  ).isDebugEnabled(  ) )
+                        {
+                            getLogger(  ).debug( "No commands available. Will 
just wait for one" );
+                        }
+
+                        m_commandStack.wait(  );
+                    }
+                }
+                catch( final InterruptedException ie )
+                {
+                    if( getLogger(  ).isDebugEnabled(  ) )
+                    {
+                        getLogger(  ).debug( "I've been interrupted" );
+                    }
+                }
+
+                if( m_keepRunning )
+                {
+                    if( m_commandStack.size(  ) > 0 )
+                    {
+                        final ExecutionInfo info =
+                            (ExecutionInfo)m_commandStack.first(  );
+                        final long delay =
+                            info.m_nextRun - System.currentTimeMillis(  );
+
+                        if( delay < 0 )
+                        {
+                            info.execute(  );
+                        }
+                    }
+                }
+            }
+        }
+
+        if( getLogger(  ).isDebugEnabled(  ) )
+        {
+            getLogger(  ).debug( "Exiting loop" );
+        }
+    }
+
+    /**
+     * Start the managing thread
+     *
+     * @throws Exception DOCUMENT ME!
+     */
+    public void start(  )
+        throws Exception
+    {
+        if( getLogger(  ).isDebugEnabled(  ) )
+        {
+            getLogger(  ).debug( "starting heart" );
+        }
+
+        m_keepRunning = true;
+        ( (ThreadPool)m_pools.get( DEFAULT_THREADPOOL_NAME ) ).execute( this );
+    }
+
+    /**
+     * Stop the managing thread
+     *
+     * @throws Exception DOCUMENT ME!
+     */
+    public void stop(  )
+        throws Exception
+    {
+        m_keepRunning = false;
+
+        synchronized( m_commandStack )
+        {
+            m_commandStack.notifyAll(  );
+        }
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param priority The priority to set as string value.
+     *
+     * @return The priority as int value.
+     */
+    private int getPriority( final String priority )
+    {
+        if( "MIN".equalsIgnoreCase( priority ) )
+        {
+            return Thread.MIN_PRIORITY;
+        }
+        else if( "NORM".equalsIgnoreCase( priority ) )
+        {
+            return Thread.NORM_PRIORITY;
+        }
+        else if( "MAX".equalsIgnoreCase( priority ) )
+        {
+            return Thread.MAX_PRIORITY;
+        }
+        else
+        {
+            getLogger(  ).warn( "Unknown thread priority \"" + priority +
+                                "\". Set to \"NORM\"." );
+
+            return Thread.NORM_PRIORITY;
+        }
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param config DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     *
+     * @throws ConfigurationException DOCUMENT ME!
+     */
+    private DefaultThreadPool configThreadPool( final Configuration config )
+        throws ConfigurationException
+    {
+        final String name = config.getChild( "name" ).getValue(  );
+        final int queueSize =
+            config.getChild( "queue-size" ).getValueAsInteger( 
DEFAULT_QUEUE_SIZE );
+        final int maxPoolSize =
+            config.getChild( "max-pool-size" ).getValueAsInteger( 
DEFAULT_MAX_POOL_SIZE );
+        int minPoolSize =
+            config.getChild( "min-pool-size" ).getValueAsInteger( 
DEFAULT_MIN_POOL_SIZE );
+
+        // make sure we have enough threads for the default thread pool as we 
+        // need one for ourself
+        if( DEFAULT_THREADPOOL_NAME.equals( name ) &&
+            ( ( minPoolSize > 0 ) && ( minPoolSize < DEFAULT_MIN_POOL_SIZE ) ) 
)
+        {
+            minPoolSize = DEFAULT_MIN_POOL_SIZE;
+        }
+
+        final String priority =
+            config.getChild( "priority" ).getValue( DEFAULT_THREAD_PRIORITY );
+        final boolean isDaemon =
+            config.getChild( "daemon" ).getValueAsBoolean( DEFAULT_DAEMON_MODE 
);
+        final long keepAliveTime =
+            config.getChild( "keep-alive-time-ms" ).getValueAsLong( 
DEFAULT_KEEP_ALIVE_TIME );
+        final String blockPolicy =
+            config.getChild( "block-policy" ).getValue( 
DefaultThreadPool.POLICY_DEFAULT );
+        final boolean shutdownGraceful =
+            config.getChild( "shutdown-graceful" ).getValueAsBoolean( 
DEFAULT_SHUTDOWN_GRACEFUL );
+        final int shutdownWaitTime =
+            config.getChild( "shutdown-wait-time-ms" ).getValueAsInteger( 
DEFAULT_SHUTDOWN_WAIT_TIME );
+
+        return createPool( new DefaultThreadPool(  ), name, queueSize,
+                           maxPoolSize, minPoolSize, getPriority( priority ),
+                           isDaemon, keepAliveTime, blockPolicy,
+                           shutdownGraceful, shutdownWaitTime );
+    }
+
+    /**
+     * Create a ThreadPool
+     *
+     * @param pool DOCUMENT ME!
+     * @param name DOCUMENT ME!
+     * @param queueSize The size of the queue
+     * @param maxPoolSize The maximum number of threads
+     * @param minPoolSize The maximum number of threads
+     * @param priority The priority of threads created by this pool. This is
+     *        one of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *        Thread#NORM_PRIORITY}, or [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     * @param isDaemon Whether or not thread from the pool should run in daemon
+     *        mode
+     * @param keepAliveTime How long should a thread be alive for new work to
+     *        be done before it is GCed
+     * @param blockPolicy What's the blocking policy is resources are exhausted
+     * @param shutdownGraceful Should we wait for the queue to finish all
+     *        pending commands?
+     * @param shutdownWaitTime After what time a normal shutdown should take
+     *        into account if a graceful shutdown has not come to an end
+     *
+     * @return A newly created <code>ThreadPool</code>
+     */
+    private DefaultThreadPool createPool( final DefaultThreadPool pool,
+                                          final String name,
+                                          final int queueSize,
+                                          final int maxPoolSize,
+                                          final int minPoolSize,
+                                          final int priority,
+                                          final boolean isDaemon,
+                                          final long keepAliveTime,
+                                          final String blockPolicy,
+                                          final boolean shutdownGraceful,
+                                          final int shutdownWaitTime )
+    {
+        pool.enableLogging( getLogger(  ).getChildLogger( name ) );
+        pool.setName( name );
+
+        ThreadFactory factory = null;
+
+        try
+        {
+            factory =
+                (ThreadFactory)m_defaultThreadFactoryClass.newInstance(  );
+        }
+        catch( final Exception ex )
+        {
+            getLogger(  ).warn( "Cannot instantiate a ThreadFactory from class 
" +
+                                m_defaultThreadFactoryClass.getName(  ) +
+                                ". Will use a " +
+                                DefaultThreadFactory.class.getName(  ), ex );
+            factory = new DefaultThreadFactory(  );
+        }
+
+        factory.setPriority( priority );
+        factory.setDaemon( isDaemon );
+        pool.setThreadFactory( factory );
+        pool.setQueue( queueSize );
+        pool.setMaximumPoolSize( ( maxPoolSize < 0 ) ? Integer.MAX_VALUE
+                                 : maxPoolSize );
+
+        if( minPoolSize < 1 )
+        {
+            getLogger(  ).warn( "min-pool-size < 1 for pool \"" +
+                                name + "\". Set to 1" );
+        }
+
+        pool.setMinimumPoolSize( ( minPoolSize < 1 ) ? 1 : minPoolSize );
+
+        if( keepAliveTime < 0 )
+        {
+            getLogger(  ).warn( "keep-alive-time-ms < 0 for pool \"" +
+                                name + "\". Set to 1000" );
+        }
+
+        pool.setKeepAliveTime( ( keepAliveTime < 0 ) ? 1000 : keepAliveTime );
+        pool.setBlockPolicy( blockPolicy );
+        pool.setShutdownGraceful( shutdownGraceful );
+        pool.setShutdownWaitTimeMs( shutdownWaitTime );
+
+        synchronized( m_pools )
+        {
+            m_pools.put( name, pool );
+        }
+
+        printPoolInfo( pool );
+
+        return pool;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param pool DOCUMENT ME!
+     */
+    private void printPoolInfo( final DefaultThreadPool pool )
+    {
+        if( getLogger(  ).isInfoEnabled(  ) )
+        {
+            if( pool.isQueued(  ) )
+            {
+                final StringBuffer msg = new StringBuffer(  );
+                msg.append( "ThreadPool named \"" ).append( pool.getName(  ) );
+                msg.append( "\" created with maximum queue-size=" );
+                msg.append( pool.getMaxQueueSize(  ) );
+                msg.append( ",max-pool-size=" ).append( 
pool.getMaximumPoolSize(  ) );
+                msg.append( ",min-pool-size=" ).append( 
pool.getMinimumPoolSize(  ) );
+                msg.append( ",priority=" ).append( pool.getPriority(  ) );
+                msg.append( ",isDaemon=" ).append( ( 
(ThreadFactory)pool.getThreadFactory(  ) ).isDaemon(  ) );
+                msg.append( ",keep-alive-time-ms=" ).append( 
pool.getKeepAliveTime(  ) );
+                msg.append( ",block-policy=\"" ).append( pool.getBlockPolicy(  
) );
+                msg.append( "\",shutdown-wait-time-ms=" ).append( 
pool.getShutdownWaitTimeMs(  ) );
+                getLogger(  ).info( msg.toString(  ) );
+            }
+            else
+            {
+                final StringBuffer msg = new StringBuffer(  );
+                msg.append( "ThreadPool named \"" ).append( pool.getName(  ) );
+                msg.append( "\" created with no queue,max-pool-size=" 
).append( pool.getMaximumPoolSize(  ) );
+                msg.append( ",min-pool-size=" ).append( 
pool.getMinimumPoolSize(  ) );
+                msg.append( ",priority=" ).append( pool.getPriority(  ) );
+                msg.append( ",isDaemon=" ).append( ( 
(ThreadFactory)pool.getThreadFactory(  ) ).isDaemon(  ) );
+                msg.append( ",keep-alive-time-ms=" ).append( 
pool.getKeepAliveTime(  ) );
+                msg.append( ",block-policy=" ).append( pool.getBlockPolicy(  ) 
);
+                msg.append( ",shutdown-wait-time-ms=" ).append( 
pool.getShutdownWaitTimeMs(  ) );
+                getLogger(  ).info( msg.toString(  ) );
+            }
+        }
+    }
+
+    //~ Inner Classes 
----------------------------------------------------------
+
+    /**
+     * The $classType$ class ...
+     *
+     * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+     * @version $Id: DefaultRunnableManager.java 56848 2004-11-07 14:09:23Z 
giacomo $
+     */
+    private class ExecutionInfo
+        implements Comparable
+    {
+        //~ Instance fields 
----------------------------------------------------
+
+        /** Our logger */
+        final Logger m_logger;
+
+        /** DOCUMENT ME! */
+        final Runnable m_command;
+
+        /** DOCUMENT ME! */
+        final ThreadPool m_pool;
+
+        /** DOCUMENT ME! */
+        final long m_delay;
+
+        /** DOCUMENT ME! */
+        final long m_interval;
+
+        /** DOCUMENT ME! */
+        long m_nextRun = 0;
+
+        //~ Constructors 
-------------------------------------------------------
+
+        /**
+         * Creates a new ExecutionInfo object.
+         *
+         * @param pool DOCUMENT ME!
+         * @param command DOCUMENT ME!
+         * @param delay DOCUMENT ME!
+         * @param interval DOCUMENT ME!
+         * @param logger DOCUMENT ME!
+         */
+        ExecutionInfo( final ThreadPool pool,
+                       final Runnable command,
+                       final long delay,
+                       final long interval,
+                       final Logger logger )
+        {
+            m_pool = pool;
+            m_command = command;
+            m_delay = delay;
+            m_interval = interval;
+            m_logger = logger;
+            m_nextRun = System.currentTimeMillis(  ) + delay;
+
+            synchronized( m_commandStack )
+            {
+                m_commandStack.add( this );
+                m_commandStack.notifyAll(  );
+            }
+            Thread.yield(); // Give others a chance to run
+        }
+
+        //~ Methods 
------------------------------------------------------------
+
+        /**
+         * DOCUMENT ME!
+         *
+         * @param other DOCUMENT ME!
+         *
+         * @return DOCUMENT ME!
+         */
+        public int compareTo( final Object other )
+        {
+            final ExecutionInfo otherInfo = (ExecutionInfo)other;
+
+            return (int)( m_nextRun - otherInfo.m_nextRun );
+        }
+
+        /**
+         * DOCUMENT ME!
+         */
+        void execute(  )
+        {
+            if( getLogger(  ).isDebugEnabled(  ) )
+            {
+                getLogger(  ).debug( "Hand over Command " +
+                                     m_command.toString(  ) + " to pool \"" +
+                                     m_pool.getName(  ) + "\" with delay=" + 
m_delay +
+                                     " and interval=" + m_interval );
+            }
+
+            synchronized( m_commandStack )
+            {
+                m_commandStack.remove( this );
+                m_nextRun = ( ( m_interval > 0 )
+                              ? ( System.currentTimeMillis(  ) + m_interval ) 
: 0 );
+
+                if( m_nextRun > 0 )
+                {
+                    m_commandStack.add( this );
+                }
+            }
+
+            try
+            {
+                m_pool.execute( m_command );
+            }
+            catch( final InterruptedException ie )
+            {
+                if( m_logger.isDebugEnabled(  ) )
+                {
+                    m_logger.debug( m_command + " has been interrupted" );
+                }
+            }
+            catch( final Throwable t )
+            {
+                m_logger.error( "Exception thrown by Command " + m_command, t 
);
+            }
+        }
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultThreadFactory.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultThreadFactory.java
 Mon Nov  8 05:16:55 2004
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * This class is responsible to create new Thread instances to run a command.
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Otego AG, Switzerland</a>
+ * @version $Id: DefaultThreadFactory.java 56765 2004-11-06 13:54:31Z giacomo $
+ */
+public class DefaultThreadFactory
+    implements ThreadFactory, EDU.oswego.cs.dl.util.concurrent.ThreadFactory
+{
+    //~ Instance fields 
--------------------------------------------------------
+
+    /** The daemon mode */
+    private boolean m_isDaemon = false;
+
+    /** The priority of newly created Threads */
+    private int m_priority = Thread.NORM_PRIORITY;
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * Set the isDaemon property
+     *
+     * @param isDaemon Whether or not new <code>Thread</code> should run as
+     *        daemons.
+     */
+    public void setDaemon( boolean isDaemon )
+    {
+        m_isDaemon = isDaemon;
+    }
+
+    /**
+     * Get the isDaemon property
+     *
+     * @return Whether or not new <code>Thread</code> will run as daemons.
+     */
+    public boolean isDaemon(  )
+    {
+        return m_isDaemon;
+    }
+
+    /**
+     * Set the priority newly created <code>Thread</code>s should have
+     *
+     * @param priority One of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL 
PROTECTED]
+     *        Thread#NORM_PRIORITY}, [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     */
+    public void setPriority( final int priority )
+    {
+        if( ( Thread.MAX_PRIORITY == priority ) ||
+            ( Thread.MIN_PRIORITY == priority ) ||
+            ( Thread.NORM_PRIORITY == priority ) )
+        {
+            m_priority = priority;
+        }
+    }
+
+    /**
+     * Get the priority newly created <code>Thread</code>s will have
+     *
+     * @return One of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *         Thread#NORM_PRIORITY}, [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     */
+    public int getPriority(  )
+    {
+        return m_priority;
+    }
+
+    /**
+     * Create a new Thread for Runnable
+     *
+     * @param command The [EMAIL PROTECTED] Runnable}
+     *
+     * @return A new Thread instance
+     */
+    public Thread newThread( final Runnable command )
+    {
+        final Thread thread = new Thread( command );
+        thread.setPriority( m_priority );
+        thread.setDaemon( m_isDaemon );
+
+        return thread;
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultThreadPool.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/DefaultThreadPool.java
    Mon Nov  8 05:16:55 2004
@@ -0,0 +1,378 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+import org.apache.avalon.framework.logger.LogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+
+import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
+
+
+/**
+ * The DefaultThreadPool class implements the [EMAIL PROTECTED] ThreadPool} 
interface.
+ * Instances of this class are made by the [EMAIL PROTECTED] RunnableManager} 
passing a
+ * configuration into the <code>configure</code> method.
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version CVS $Id: DefaultThreadPool.java 56843 2004-11-07 13:34:30Z giacomo 
$
+ */
+public class DefaultThreadPool
+    extends PooledExecutor
+    implements ThreadPool, LogEnabled
+{
+    //~ Static fields/initializers 
---------------------------------------------
+
+    /** Default ThreadPool block policy */
+    public static final String POLICY_DEFAULT = POLICY_RUN;
+
+    //~ Instance fields 
--------------------------------------------------------
+
+    /** Wrapps a channel */
+    private ChannelWrapper m_channelWrapper;
+
+    /** Our logger */
+    private Logger m_logger;
+
+    /** The Queue */
+    private Queue m_queue;
+
+    /** The blocking policy */
+    private String m_blockPolicy;
+
+    /** The name of this thread pool */
+    private String m_name;
+
+    /** Should we wait for running jobs to terminate on shutdown ? */
+    private boolean m_shutdownGraceful;
+
+    /** The priority of threads */
+    private int m_priority;
+
+    /** The maximum queue size */
+    private int m_queueSize;
+
+    /** How long to wait for running jobs to terminate on disposition */
+    private int m_shutdownWaitTimeMs;
+
+    //~ Constructors 
-----------------------------------------------------------
+
+    /**
+     * Create a new pool.
+     */
+    DefaultThreadPool(  )
+    {
+        this( new ChannelWrapper(  ) );
+    }
+
+    /**
+     * Create a new pool.
+     *
+     * @param channel DOCUMENT ME!
+     */
+    private DefaultThreadPool( final ChannelWrapper channel )
+    {
+        super( channel );
+        m_channelWrapper = channel;
+    }
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return Returns the blockPolicy.
+     */
+    public String getBlockPolicy(  )
+    {
+        return m_blockPolicy;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return maximum size of the queue (0 if isQueued() == false)
+     *
+     * @see org.apache.cocoon.components.thread.ThreadPool#getQueueSize()
+     */
+    public int getMaxQueueSize(  )
+    {
+        return ( ( m_queueSize < 0 ) ? Integer.MAX_VALUE : m_queueSize );
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return size of queue (0 if isQueued() == false)
+     *
+     * @see org.apache.cocoon.components.thread.ThreadPool#getQueueSize()
+     */
+    public int getMaximumQueueSize(  )
+    {
+        return m_queueSize;
+    }
+
+    /**
+     * @see org.apache.cocoon.components.thread.ThreadPool#getName()
+     */
+    public String getName(  )
+    {
+        return m_name;
+    }
+
+    /**
+     * Get hte priority used to create Threads
+     *
+     * @return [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED] 
Thread#NORM_PRIORITY}, or
+     *         [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     */
+    public int getPriority(  )
+    {
+        return m_priority;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return current size of the queue (0 if isQueued() == false)
+     *
+     * @see org.apache.cocoon.components.thread.ThreadPool#getQueueSize()
+     */
+    public int getQueueSize(  )
+    {
+        return m_queue.getQueueSize(  );
+    }
+
+    /**
+     * Whether this DefaultThreadPool has a queue
+     *
+     * @return Returns the m_isQueued.
+     *
+     * @see org.apache.cocoon.components.thread.ThreadPool#isQueued()
+     */
+    public boolean isQueued(  )
+    {
+        return m_queueSize != 0;
+    }
+
+    /**
+     * Set the logger
+     *
+     * @param logger
+     *
+     * @see 
org.apache.avalon.framework.logger.LogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger)
+     */
+    public void enableLogging( Logger logger )
+    {
+        m_logger = logger;
+    }
+
+    /**
+     * Execute a command
+     *
+     * @param command The [EMAIL PROTECTED] Runnable} to execute
+     *
+     * @throws InterruptedException In case of interruption
+     */
+    public void execute( Runnable command )
+        throws InterruptedException
+    {
+        if( getLogger(  ).isDebugEnabled(  ) )
+        {
+            getLogger(  ).debug( "Executing Command: " + command.toString(  ) +
+                                 ",pool=" + getName(  ) );
+        }
+
+        super.execute( command );
+    }
+
+    /**
+     * @see org.apache.cocoon.components.thread.ThreadPool#shutdownGraceful()
+     */
+    public void shutdown(  )
+    {
+        if( m_shutdownGraceful )
+        {
+            shutdownAfterProcessingCurrentlyQueuedTasks(  );
+        }
+        else
+        {
+            shutdownNow(  );
+        }
+
+        try
+        {
+            if( getShutdownWaitTimeMs(  ) > 0 )
+            {
+                if( ! awaitTerminationAfterShutdown( getShutdownWaitTimeMs(  ) 
) )
+                {
+                    getLogger(  ).warn( "running commands have not terminated 
within " +
+                                        getShutdownWaitTimeMs(  ) +
+                                        "ms. Will shut them down by 
interruption" );
+                    interruptAll(  );
+                    shutdownNow(  );
+                }
+            }
+
+            awaitTerminationAfterShutdown(  );
+        }
+        catch( final InterruptedException ie )
+        {
+            getLogger(  ).error( "cannot shutdown ThreadPool", ie );
+        }
+    }
+
+    /**
+     * Set the blocking policy
+     *
+     * @param blockPolicy The blocking policy value
+     */
+    void setBlockPolicy( final String blockPolicy )
+    {
+        m_blockPolicy = blockPolicy;
+
+        if( POLICY_ABORT.equalsIgnoreCase( blockPolicy ) )
+        {
+            abortWhenBlocked(  );
+        }
+        else if( POLICY_DISCARD.equalsIgnoreCase( blockPolicy ) )
+        {
+            discardWhenBlocked(  );
+        }
+        else if( POLICY_DISCARD_OLDEST.equalsIgnoreCase( blockPolicy ) )
+        {
+            discardOldestWhenBlocked(  );
+        }
+        else if( POLICY_RUN.equalsIgnoreCase( blockPolicy ) )
+        {
+            runWhenBlocked(  );
+        }
+        else if( POLICY_WAIT.equalsIgnoreCase( blockPolicy ) )
+        {
+            waitWhenBlocked(  );
+        }
+        else
+        {
+            final StringBuffer msg = new StringBuffer(  );
+            msg.append( "WARNING: Unknown block-policy configuration \"" )
+               .append( blockPolicy );
+            msg.append( "\". Should be one of \"" ).append( POLICY_ABORT );
+            msg.append( "\",\"" ).append( POLICY_DISCARD );
+            msg.append( "\",\"" ).append( POLICY_DISCARD_OLDEST );
+            msg.append( "\",\"" ).append( POLICY_RUN );
+            msg.append( "\",\"" ).append( POLICY_WAIT );
+            msg.append( "\". Will use \"" ).append( POLICY_DEFAULT ).append( 
"\"" );
+            getLogger(  ).warn( msg.toString(  ) );
+            setBlockPolicy( POLICY_DEFAULT );
+        }
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param name The name to set.
+     */
+    void setName( String name )
+    {
+        m_name = name;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param priority The priority to set.
+     */
+    void setPriority( final int priority )
+    {
+        m_priority = priority;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param queueSize DOCUMENT ME!
+     */
+    void setQueue( final int queueSize )
+    {
+        if( queueSize != 0 )
+        {
+            if( queueSize > 0 )
+            {
+                m_queue = new BoundedQueue( queueSize );
+            }
+            else
+            {
+                m_queue = new LinkedQueue(  );
+            }
+        }
+        else
+        {
+            m_queue = new SynchronousChannel(  );
+        }
+
+        m_queueSize = queueSize;
+        m_channelWrapper.setChannel( m_queue );
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param shutdownGraceful The shutdownGraceful to set.
+     */
+    void setShutdownGraceful( boolean shutdownGraceful )
+    {
+        m_shutdownGraceful = shutdownGraceful;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return Returns the shutdownGraceful.
+     */
+    boolean isShutdownGraceful(  )
+    {
+        return m_shutdownGraceful;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param shutdownWaitTimeMs The shutdownWaitTimeMs to set.
+     */
+    void setShutdownWaitTimeMs( int shutdownWaitTimeMs )
+    {
+        m_shutdownWaitTimeMs = shutdownWaitTimeMs;
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return Returns the shutdownWaitTimeMs.
+     */
+    int getShutdownWaitTimeMs(  )
+    {
+        return m_shutdownWaitTimeMs;
+    }
+
+    /**
+     * Get our <code>Logger</code>
+     *
+     * @return our <code>Logger</code>
+     */
+    private Logger getLogger(  )
+    {
+        return m_logger;
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/LinkedQueue.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/LinkedQueue.java
  Mon Nov  8 05:16:55 2004
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * A linked list based channel implementation. The algorithm avoids contention
+ * between puts and takes when the queue is not empty. Normally a put and a
+ * take can proceed simultaneously. (Although it does not allow multiple
+ * concurrent puts or takes.) This class tends to perform more efficently than
+ * other Queue implementations in producer/consumer applications.
+ * 
+ * <p>
+ * [<a
+ * 
href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html";>
+ * Introduction to this package. </a>]
+ * </p>
+ */
+public class LinkedQueue
+    extends EDU.oswego.cs.dl.util.concurrent.LinkedQueue
+    implements Queue
+{
+    //~ Instance fields 
--------------------------------------------------------
+
+    /** The size */
+    protected int m_size = 0;
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * @see org.apache.cocoon.components.thread.Queue#getQueueSize()
+     */
+    public int getQueueSize(  )
+    {
+        return m_size;
+    }
+
+    /**
+     * @see EDU.oswego.cs.dl.util.concurrent.LinkedQueue#extract()
+     */
+    protected synchronized Object extract(  )
+    {
+        synchronized( head_ )
+        {
+            if( head_.next != null )
+            {
+                --m_size;
+            }
+
+            return super.extract(  );
+        }
+    }
+
+    /**
+     * @see 
EDU.oswego.cs.dl.util.concurrent.LinkedQueue#insert(java.lang.Object)
+     */
+    protected void insert( final Object object )
+    {
+        super.insert( object );
+        ++m_size;
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/Queue.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/Queue.java
        Mon Nov  8 05:16:55 2004
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * Extension to add queue size reporting
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version CVS $Id: Queue.java 56702 2004-11-05 22:52:05Z giacomo $
+ *
+ * @see EDU.oswego.cs.dl.util.concurrent.Channel
+ */
+public interface Queue
+    extends EDU.oswego.cs.dl.util.concurrent.Channel
+{
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * get the current queue size
+     *
+     * @return The current queue size
+     */
+    int getQueueSize(  );
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/RunnableManager.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/RunnableManager.java
      Mon Nov  8 05:16:55 2004
@@ -0,0 +1,162 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * The RunnableManager interface describes the functionality of an
+ * implementation running commands in the background.
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version CVS $Id: RunnableManager.java 56786 2004-11-06 23:12:39Z giacomo $
+ */
+public interface RunnableManager
+{
+    //~ Instance fields 
--------------------------------------------------------
+
+    /** The role name */
+    String ROLE = RunnableManager.class.getName(  );
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * Create a shared ThreadPool with a specific [EMAIL PROTECTED] 
ThreadFactory}
+     *
+     * @param name The name of the thread pool
+     * @param queueSize The size of the queue
+     * @param maxPoolSize The maximum number of threads
+     * @param minPoolSize The maximum number of threads
+     * @param priority The priority of threads created by this pool. This is
+     *        one of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *        Thread#NORM_PRIORITY}, or [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     * @param isDaemon Whether or not thread from the pool should run in daemon
+     *        mode
+     * @param keepAliveTime How long should a thread be alive for new work to
+     *        be done before it is GCed
+     * @param blockPolicy What's the blocking policy is resources are exhausted
+     * @param shutdownGraceful Should we wait for the queue to finish all
+     *        pending commands?
+     * @param shutdownWaitTime After what time a normal shutdown should take
+     *        into account if a graceful shutdown has not come to an end
+     */
+    void createPool( String name,
+                     int queueSize,
+                     int maxPoolSize,
+                     int minPoolSize,
+                     int priority,
+                     final boolean isDaemon,
+                     long keepAliveTime,
+                     String blockPolicy,
+                     boolean shutdownGraceful,
+                     int shutdownWaitTime );
+
+    /**
+     * Create a private ThreadPool with a specific [EMAIL PROTECTED] 
ThreadFactory}
+     *
+     * @param queueSize The size of the queue
+     * @param maxPoolSize The maximum number of threads
+     * @param minPoolSize The maximum number of threads
+     * @param priority The priority of threads created by this pool. This is
+     *        one of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *        Thread#NORM_PRIORITY}, or [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     * @param isDaemon Whether or not thread from the pool should run in daemon
+     *        mode
+     * @param keepAliveTime How long should a thread be alive for new work to
+     *        be done before it is GCed
+     * @param blockPolicy What's the blocking policy is resources are exhausted
+     * @param shutdownGraceful Should we wait for the queue to finish all
+     *        pending commands?
+     * @param shutdownWaitTime After what time a normal shutdown should take
+     *        into account if a graceful shutdown has not come to an end
+     *
+     * @return The newly created <code>ThreadPool</code>
+     */
+    ThreadPool createPool( int queueSize,
+                           int maxPoolSize,
+                           int minPoolSize,
+                           int priority,
+                           final boolean isDaemon,
+                           long keepAliveTime,
+                           String blockPolicy,
+                           boolean shutdownGraceful,
+                           int shutdownWaitTime );
+
+    /**
+     * Immediate Execution of a runnable in the background
+     *
+     * @param command The command to execute
+     */
+    void execute( Runnable command );
+
+    /**
+     * Immediate Execution of a runnable in the background
+     *
+     * @param command The command to execute
+     * @param delay The delay before first run
+     */
+    void execute( Runnable command,
+                  long delay );
+
+    /**
+     * Immediate Execution of a runnable in the background
+     *
+     * @param command The command to execute
+     * @param delay The delay before first run
+     * @param interval The interval of repeated runs
+     */
+    void execute( Runnable command,
+                  long delay,
+                  long interval );
+
+    /**
+     * Immediate Execution of a runnable in the background
+     *
+     * @param threadPoolName The thread pool to use
+     * @param command The command to execute
+     */
+    void execute( String threadPoolName,
+                  Runnable command );
+
+    /**
+     * Immediate Execution of a runnable in the background
+     *
+     * @param threadPoolName The thread pool to use
+     * @param command The command to execute
+     * @param delay The delay before first run
+     */
+    void execute( String threadPoolName,
+                  Runnable command,
+                  long delay );
+
+    /**
+     * Delayed and repeated Execution of a runnable in the background
+     *
+     * @param threadPoolName The thread pool to use
+     * @param command The command to execute
+     * @param delay The delay before first run
+     * @param interval The interval of repeated runs
+     */
+    void execute( String threadPoolName,
+                  Runnable command,
+                  long delay,
+                  long interval );
+
+    /**
+     * Remove a [EMAIL PROTECTED] Runnable} from the execution stack
+     *
+     * @param command The command to be removed
+     */
+    void remove( Runnable command );
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/SynchronousChannel.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/SynchronousChannel.java
   Mon Nov  8 05:16:55 2004
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+import EDU.oswego.cs.dl.util.concurrent.CyclicBarrier;
+import EDU.oswego.cs.dl.util.concurrent.Rendezvous;
+
+
+/**
+ * A rendezvous channel, similar to those used in CSP and Ada.  Each put must
+ * wait for a take, and vice versa.  Synchronous channels are well suited for
+ * handoff designs, in which an object running in one thread must synch up
+ * with an object running in another thread in order to hand it some
+ * information, event, or task.
+ * 
+ * <p>
+ * If you only need threads to synch up without exchanging information,
+ * consider using a Barrier. If you need bidirectional exchanges, consider
+ * using a Rendezvous.
+ * </p>
+ * 
+ * <p></p>
+ * 
+ * <p>
+ * [<a
+ * 
href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html";>
+ * Introduction to this package. </a>]
+ * </p>
+ *
+ * @see CyclicBarrier
+ * @see Rendezvous
+ */
+public class SynchronousChannel
+    extends EDU.oswego.cs.dl.util.concurrent.SynchronousChannel
+    implements Queue
+{
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * @see org.apache.cocoon.components.thread.Queue#getQueueSize()
+     */
+    public int getQueueSize(  )
+    {
+        return 0;
+    }
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ThreadFactory.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ThreadFactory.java
        Mon Nov  8 05:16:55 2004
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * The ThreadFactory interface describes the responability of Factories
+ * creating Thread for [EMAIL PROTECTED] ThreadPool}s of the [EMAIL PROTECTED] 
RunnableManager}
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version CVS $Id: ThreadFactory.java 56765 2004-11-06 13:54:31Z giacomo $
+ */
+public interface ThreadFactory
+    extends EDU.oswego.cs.dl.util.concurrent.ThreadFactory
+{
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * Set the daemon mode of created <code>Thread</code>s should have
+     *
+     * @param isDaemon Whether new [EMAIL PROTECTED] Thread}s should run as 
daemons.
+     */
+    void setDaemon( boolean isDaemon );
+
+    /**
+     * Get the daemon mode created <code>Thread</code>s will have
+     *
+     * @return Whether new [EMAIL PROTECTED] Thread}s should run as daemons.
+     */
+    boolean isDaemon(  );
+
+    /**
+     * Set the priority newly created <code>Thread</code>s should have
+     *
+     * @param priority One of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL 
PROTECTED]
+     *        Thread#NORM_PRIORITY}, [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     */
+    void setPriority( int priority );
+
+    /**
+     * Get the priority newly created <code>Thread</code>s will have
+     *
+     * @return One of [EMAIL PROTECTED] Thread#MIN_PRIORITY}, [EMAIL PROTECTED]
+     *         Thread#NORM_PRIORITY}, [EMAIL PROTECTED] Thread#MAX_PRIORITY}
+     */
+    int getPriority(  );
+
+    /**
+     * Create a new Thread for a [EMAIL PROTECTED] Runnable} command
+     *
+     * @param command The <code>Runnable</code>
+     *
+     * @return new <code>Thread</code>
+     */
+    Thread newThread( Runnable command );
+}

Added: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ThreadPool.java
==============================================================================
--- (empty file)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/thread/ThreadPool.java
   Mon Nov  8 05:16:55 2004
@@ -0,0 +1,144 @@
+/*
+ * Copyright 1999-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.cocoon.components.thread;
+
+/**
+ * The ThreadPool interface gives access to methods needed to inspect and use
+ * of  a pool of threads
+ *
+ * @author <a href="mailto:giacomo.at.apache.org";>Giacomo Pati</a>
+ * @version CVS $Id: ThreadPool.java 56702 2004-11-05 22:52:05Z giacomo $
+ */
+public interface ThreadPool
+{
+    //~ Instance fields 
--------------------------------------------------------
+
+    /** ThreadPool block policy ABORT */
+    String POLICY_ABORT = "ABORT";
+
+    /** ThreadPool block policy DISCARD */
+    String POLICY_DISCARD = "DISCARD";
+
+    /** ThreadPool block policy DISCARD-OLDEST */
+    String POLICY_DISCARD_OLDEST = "DISCARDOLDEST";
+
+    /** ThreadPool block policy RUN */
+    String POLICY_RUN = "RUN";
+
+    /** ThreadPool block policy WAIT */
+    String POLICY_WAIT = "WAIT";
+
+    /** The Role name */
+    String ROLE = ThreadPool.class.getName(  );
+
+    //~ Methods 
----------------------------------------------------------------
+
+    /**
+     * The blocking policy used
+     *
+     * @return DOCUMENT ME!
+     */
+    String getBlockPolicy(  );
+
+    /**
+     * How long will a thread in this pool be idle before it is allowed to be
+     * garbage collected
+     *
+     * @return maximum idle time
+     */
+    long getKeepAliveTime(  );
+
+    /**
+     * How many threads are in this pool at maximum
+     *
+     * @return maximum size of pool
+     */
+    int getMaximumPoolSize(  );
+
+    /**
+     * Maximum size of the queue
+     *
+     * @return current size of queue
+     */
+    int getMaximumQueueSize(  );
+
+    /**
+     * How many threads are in this pool at minimum
+     *
+     * @return minimum size of pool
+     */
+    int getMinimumPoolSize(  );
+
+    /**
+     * The Name of this thread pool
+     *
+     * @return The name
+     */
+    String getName(  );
+
+    /**
+     * How many threads are currently in this pool
+     *
+     * @return current size of pool
+     */
+    int getPoolSize(  );
+
+    /**
+     * Get the thread priority used by this pool
+     *
+     * @return current size of queue
+     */
+    int getPriority(  );
+
+    /**
+     * Current size of the queue
+     *
+     * @return current size of queue
+     */
+    int getQueueSize(  );
+
+    /**
+     * Whether this ThreadPool has a queue
+     *
+     * @return Returns true if this ThreadPool has a queue
+     */
+    boolean isQueued(  );
+
+    /**
+     * Returns true if a shutDown method has succeeded in terminating all
+     * threads
+     *
+     * @return Whether a shutDown method has succeeded in terminating all
+     *         threads
+     */
+    boolean isTerminatedAfterShutdown(  );
+
+    /**
+     * Execute a command using this pool
+     *
+     * @param command a [EMAIL PROTECTED] Runnable} to execute
+     *
+     * @throws InterruptedException In case of interruption
+     */
+    void execute( Runnable command )
+        throws InterruptedException;
+
+    /**
+     * Terminates all threads possibly awaiting processing all elements
+     * currently in queue.
+     */
+    void shutdown(  );
+}

Modified: 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/helpers/DefaultIncludeCacheManager.java
==============================================================================
--- 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/helpers/DefaultIncludeCacheManager.java
      (original)
+++ 
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/transformation/helpers/DefaultIncludeCacheManager.java
      Mon Nov  8 05:16:55 2004
@@ -34,6 +34,7 @@
 import org.apache.cocoon.components.sax.XMLSerializer;
 import org.apache.cocoon.components.sax.XMLTeePipe;
 import org.apache.cocoon.components.source.SourceUtil;
+import org.apache.cocoon.components.thread.RunnableManager;
 import org.apache.cocoon.xml.XMLConsumer;
 import org.apache.excalibur.source.Source;
 import org.apache.excalibur.source.SourceException;
@@ -41,6 +42,7 @@
 import org.apache.excalibur.source.SourceValidity;
 import org.apache.excalibur.store.Store;
 import org.xml.sax.SAXException;
+import EDU.oswego.cs.dl.util.concurrent.CountDown;
 
 /**
  * Default implementation of a [EMAIL PROTECTED] IncludeCacheManager}.
@@ -55,7 +57,7 @@
  * log, so actually cached content is never updated!
  * 
  *  @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a>
- *  @version CVS $Id: DefaultIncludeCacheManager.java,v 1.8 2004/03/18 
07:42:12 cziegeler Exp $
+ *  @version CVS $Id$
  *  @since   2.1
  */
 public final class DefaultIncludeCacheManager
@@ -108,10 +110,17 @@
                     if (this.getLogger().isDebugEnabled()) {
                         this.getLogger().debug("Booting preemptive loader: " + 
this.preemptiveLoaderURI);
                     }
-                    PreemptiveBooter thread = new PreemptiveBooter();
-                    thread.setURI(this.preemptiveLoaderURI);
-                    thread.start();
-                    Thread.yield();
+                    PreemptiveBooter thread = new PreemptiveBooter( 
this.preemptiveLoaderURI );
+                    try
+                    {
+                        final RunnableManager runnableManager = 
(RunnableManager)this.manager.lookup( RunnableManager.ROLE );
+                        runnableManager.execute( thread );
+                        this.manager.release( runnableManager );
+                    }
+                    catch( final ServiceException se )
+                    {
+                        getLogger().error( "Cannot lookup RunnableManager", se 
);
+                    }
                 }
             }
         } 
@@ -221,13 +230,8 @@
                     this.getLogger().debug("Waiting for pooled thread to 
finish loading.");
                 }
 
-                // wait
-                while (!loader.finished) {                    
-                    try {
-                        Thread.sleep(10);
-                    } catch (InterruptedException e) {
-                    }
-                }
+                // wait for it
+                loader.join();
 
                 if (this.getLogger().isDebugEnabled()) {
                     this.getLogger().debug("Pooled thread finished loading.");
@@ -414,53 +418,59 @@
         this.defaultCacheStorage = new 
StoreIncludeCacheStorageProxy(this.store, this.getLogger());
     }
 
-}
-
-final class LoaderThread implements Runnable {
-    
-    private  Source source;
-    private  XMLSerializer serializer;
-    boolean  finished;
-    Exception exception;
-    byte[]    content;
-    ServiceManager manager;
-    
-    public LoaderThread(Source source, 
-                        XMLSerializer serializer,
-                        ServiceManager manager) {
-        this.source = source;
-        this.serializer = serializer;
-        this.finished = false;
-        this.manager = manager;
-    }
-    
-    public void run() {
-        try {
-            SourceUtil.toSAX(this.source, this.serializer);
-            this.content = (byte[])this.serializer.getSAXFragment();
-        } catch (Exception local) {
-            this.exception = local;
-        } finally {
-            this.manager.release( this.serializer );
-            this.finished = true;
+    final private static class LoaderThread implements Runnable {
+        
+        final private Source source;
+        final private XMLSerializer serializer;
+        final private CountDown finished;
+        Exception exception;
+        byte[]    content;
+        final private ServiceManager manager;
+        
+        public LoaderThread(Source source, 
+                            XMLSerializer serializer,
+                            ServiceManager manager) {
+            this.source = source;
+            this.serializer = serializer;
+            this.finished = new CountDown( 1 );
+            this.manager = manager;
+        }
+        
+        public void run() {
+            try {
+                SourceUtil.toSAX(this.source, this.serializer);
+                this.content = (byte[])this.serializer.getSAXFragment();
+            } catch (Exception local) {
+                this.exception = local;
+            } finally {
+                this.manager.release( this.serializer );
+                this.finished.release();
+            }
+        }
+        
+        void join() {
+            try {
+                this.finished.acquire();
+            } catch ( final InterruptedException ie) {
+                // ignore
+            }
         }
     }
-    
-}
 
-final class PreemptiveBooter extends Thread {
+    final class PreemptiveBooter implements Runnable {
 
-    private String uri;
-    
-    void setURI(String uri) {
-        this.uri = uri;
-    }
-    
-    public void run() {
-        try {
-            URL url = new URL(this.uri);
-            url.getContent();
-        } catch (Exception ignore) {
+        private final String uri;
+        
+        public PreemptiveBooter( final String uri ) {
+            this.uri = uri;
+        }
+        
+        public void run() {
+            try {
+                URL url = new URL(this.uri);
+                url.getContent();
+            } catch (Exception ignore) {
+            }
         }
     }
-}
\ No newline at end of file
+}

Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf        
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf        Mon Nov 
 8 05:16:55 2004
@@ -547,4 +547,113 @@
                value="http://localhost:8080/cocoon/samples/cinclude/loader"/>
     -->
  </component>
+ 
+  <!--+
+      | Runnable manager
+      |
+      | this component manages commands (Runnables) executed in background 
using
+      | preconfigured pools of worker threads
+      +-->
+  <runnable-manager logger="core.runnable">
+    <!--+
+        | This is the default configuration of the runnable-manager. More 
+        | indepth information can be found at
+        | 
http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/PooledExecutor.html
+        | The following elements can be used:
+        |
+        | thread-factory:        specifies the fully qualified class name of an
+        |                        
org.apache.cocoon.components.thread.ThreadFactory
+        |                        implementation. It is responsible to create 
Thread 
+        |                        classes.
+        | thread-pools:          container element for thread-pool elements.
+        | name:                  required name of the pool.
+        | priority:              optional priority all threads of the pool 
will 
+        |                        have (the ThreadFactory will be set to this 
+        |                        priority).The possible values  are: 
+        |                          MIN:  corresponds to Thread#MIN_PRIORITY
+        |                          NORM: corresponds to Thread#NORM_PRIORITY 
(default)
+        |                          MAX:  corresponds to Thread#MAX_PRIORITY
+        | daemon:                whether newly created Threads should run in 
+        |                        daemon mode or not. Default to false.
+        | queue-size:            optional size of a queue to hold Runnables if 
the 
+        |                        pool is full. Possible values are:
+        |                          less than 0:    unbounded (default)
+        |                          equal to 0:     no queue at all
+        |                          greater than 0: size of the queue
+        | max-pool-size:         optional maximum number of threads in the 
pool. 
+        |                        Defaults to 5. 
+        |                        NOTE: if a queue is specified (queue-sie != 
0) 
+        |                              this value will be ignored.
+        | min-pool-size:         optional minimum number of threads in the 
pool. 
+        |                        Defaults to 5. 
+        |                        NOTE: if a queue has been specified 
(queue-sie != 0)
+        |                              this value will be used as the maximum 
of
+        |                              thread running concurrently.
+        | keep-alive-time-ms:    The time in ms an idle thread should keep 
alive 
+        |                        before it might get garbage collected. This 
+        |                        defaults to 60000 ms.
+        | block-policy;          The policy to be used if all resources 
(thread in
+        |                        the pool and slots in the queue) are 
exhausted.
+        |                        Possible values are:
+        |                          ABORT:         Throw a RuntimeException
+        |                          DISCARD:       Throw away the current 
request 
+        |                                         and return.
+        |                          DISCARDOLDEST: Throw away the oldest 
request 
+        |                                         and return.
+        |                          RUN (default): The thread making the 
execute 
+        |                                         request runs the task 
itself. 
+        |                                         This policy helps guard 
against 
+        |                                         lockup.
+        |                          WAIT:          Wait until a thread becomes 
+        |                                         available. This policy 
should, in 
+        |                                         general, not be used if the 
+        |                                         minimum number of threads is 
zero, 
+        |                                         in which case a thread may 
never 
+        |                                         become available.
+        | shutdown-graceful:     Terminate thread pool after processing all 
+        |                        Runnables currently in queue. Any Runnable 
entered 
+        |                        after this point will be discarded. A shut 
down 
+        |                        pool cannot be restarted. This also means 
that a 
+        |                        pool will need keep-alive-time-ms to 
terminate. 
+        |                        The default value not to shutdown graceful.
+        | shutdown-wait-time-ms: The time in ms to wait before issuing an 
+        |                        immediate shutdown after a graceful shutdown 
+        |                        has been requested.
+        +-->
+    
<thread-factory>org.apache.cocoon.components.thread.DefaultThreadFactory</thread-factory>
+    <thread-pools>
+      <!--+
+          | This is the default thread pool. It's use fits best for short 
+          | running background tasks.
+          +-->
+      <thread-pool>  
+        <name>default</name>
+        <priority>NORM</priority>
+        <daemon>false</daemon>
+        <queue-size>-1</queue-size>
+        <max-pool-size>5</max-pool-size>
+        <min-pool-size>5</min-pool-size>
+        <keep-alive-time-ms>60000</keep-alive-time-ms>
+        <block-policy>RUN</block-policy>
+        <shutdown-graceful>false</shutdown-graceful>
+        <shutdown-wait-time-ms>-1</shutdown-wait-time-ms>
+      </thread-pool> 
+      <!--+
+          | This thread pool should be used for daemons (permanently running 
+          | threads).
+          +-->
+      <thread-pool>  
+        <name>daemon</name>
+        <priority>NORM</priority>
+        <daemon>true</daemon>
+        <queue-size>0</queue-size>
+        <max-pool-size>-1</max-pool-size>
+        <min-pool-size>1</min-pool-size>
+        <keep-alive-time-ms>60000</keep-alive-time-ms>
+        <block-policy>ABORT</block-policy>
+        <shutdown-graceful>false</shutdown-graceful>
+        <shutdown-wait-time-ms>-1</shutdown-wait-time-ms>
+      </thread-pool> 
+    </thread-pools>
+  </runnable-manager>
 </cocoon>

Modified: cocoon/branches/BRANCH_2_1_X/status.xml
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml     (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml     Mon Nov  8 05:16:55 2004
@@ -199,6 +199,11 @@
 
   <changes>
  <release version="@version@" date="@date@">
+   <action dev="GP" type="add">
+     Added replacement for Excalibur Event package in 
org.apache.cocoon.components.thread and migrated most 
+     classes using their own threads to that package
+   </action>
+
    <action dev="AG" type="update">
      Updated antlr to 2.7.4, db-ojb to 1.0.1
    </action>

Reply via email to