Initial revision


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/pool/trunk@139576 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..3995981
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,2 @@
+build
+build.properties
\ No newline at end of file
diff --git a/PROPOSAL.html b/PROPOSAL.html
new file mode 100644
index 0000000..0c98c03
--- /dev/null
+++ b/PROPOSAL.html
@@ -0,0 +1,85 @@
+<html>
+<head>
+<title>Proposal for Pool Package</title>
+</head>
+<body bgcolor="white">
+
+<div align="center">
+<h1>Proposal for <em>Pool</em> Package</h1>
+</div>
+
+<h3>(0) Rationale</h3>
+<p>
+   Often a Java application makes use of instances or resources that
+   are expensive to create or that need to be limited due to
+   external constraints.  Database and other network connections are
+   leading, although certainly not the only examples of this
+   situation. A pool of instances can be helpful in these circumstances.
+</p>
+<p>
+   A Commons package for object pooling would give committers an
+   opportunity to coordinate their efforts and create and maintain an
+   efficient, feature-rich package under the ASF licence.
+</p>
+
+<h3>(1) Scope of the Package</h3>
+<p>
+   The package will create and maintain an object (instance) pooling
+   package to be distributed under the ASF license.
+</p>
+<p>
+   The package should support a variety of pool implementations, but
+   encourage support of an interface that makes these implementations
+   interchangeable.
+</p>
+
+<h3>(1.5) Interaction With Other Packages</h3>
+
+<p><em>Pool</em> relies upon on standard JDK 1.2 (or later) and
+the Commons-Collections APIs for production deployment.  It utilizes the
+JUnit unit testing framework for developing and executing unit tests, but
+this is of interest only to developers of the component.  Pool
+will also be a dependency for several future proposed components
+for the Jakarta Commons subproject.
+
+<p>No external configuration files are utilized.</p>
+
+<h3>(2) Initial Source of the Package</h3>
+
+<p>
+   The initial codebase was contributed by Rodney Waldhoff from
+   a working project and can be distributed under the Apache license.
+</p>
+
+<p>The proposed package name for the new component is
+<code>org.apache.commons.pool</code>.</p>
+
+
+<h3>(3)  Required Jakarta-Commons Resources</h3>
+
+<ul>
+<li>CVS Repository - New directory <code>pool</code> in the
+    <code>jakarta-commons</code> CVS repository.  All initial committers
+    are already committers on <code>jakarta-commons</code>, so no
+    additional user setups are required.</li>
+<li>Mailing List - Discussions will take place on the general
+    <em>jakarta-commons@jakarta.apache.org</em> mailing list.  To help
+    list subscribers identify messages of interest, it is suggested that
+    the message subject of messages about this component be prefixed with
+    [Pool].</li>
+<li>Bugzilla - New component "Pool" under the "Commons" product
+    category, with appropriate version identifiers as needed.</li>
+<li>Jyve FAQ - New category "commons-pool" (when available).
+</ul>
+
+
+<h3>(4) Initial Committers</h3>
+<ul>
+   <li>Morgan Delagrange</li>
+   <li>Geir Magnusson Jr.</li>
+   <li>Craig R. McClanahan</li>
+   <li>Rodney Waldhoff</li>
+   <li>David Weinrich</li>
+</ul>
+</body>
+</html>
diff --git a/STATUS.html b/STATUS.html
new file mode 100644
index 0000000..8c3a881
--- /dev/null
+++ b/STATUS.html
@@ -0,0 +1,110 @@
+<html>
+<head>
+<title>Status File for Jakarta Commons "Pool" Package</title>
+<head>
+<body bgcolor="white">
+
+
+<div align="center">
+<h1>The Jakarta Commons <em>Pool</em> Package</h1>
+$Id: STATUS.html,v 1.1 2001/04/14 16:40:32 rwaldhoff Exp $<br>
+<a href="#Introduction">[Introduction]</a>
+<a href="#Dependencies">[Dependencies]</a>
+<a href="#Release Info">[Release Info]</a>
+<a href="#Committers">[Committers]</a>
+<a href="#Action Items">[Action Items]</a>
+<br><br>
+</div>
+
+
+<a name="Introduction"></a>
+<h3>1.  INTRODUCTION</h3>
+
+<p>The <em>Pool</em> package defines a generalized object pooling interfaces,
+and provides several general-purpose implementations.
+The following major classes are provided:
+
+<ul>
+<li><strong>ObjectPool</strong> - the basic pooling interface, defining
+    borrowObject, returnObject and several optional methods.</li>
+<li><strong>KeyedObjectPool</strong> - a pooling interface that supports pooling
+    multiple <em>typed</em> objects.</li>
+<li><strong>PoolableObjectFactory</strong> - interface defining
+    lifecycle methods for object instances used within an ObjectPool.
+<li><strong>KeyedPoolableObjectFactory</strong> - interface defining
+    lifecycle methods for object instances used within a KeyedObjectPool.
+<li><strong>StackObjectPool/StackKeyedObjectPool</strong> - implementations
+    of ObjectPool and KeyedObjectPool backed by a simple Stack of
+    pooled instances</li>
+<li><strong>GenericObjectPool/GenericKeyedObjectPool</strong> - more
+    complex implementations of ObjectPool and KeyedObjectPool, supporting
+    asynchronous eviction of pooled instances, etc.</li>
+</ul>
+
+<a name="Dependencies"></a>
+<h3>2.  DEPENDENCIES</h3>
+
+<p>The <em>Pool</em> package is dependent upon the following external
+components for development and use:</p>
+<ul>
+<li><a href="http://java.sun.com/j2se">Java Development Kit</a>
+    (Version 1.2 or later)</li>
+<li>Jakarta-Commons Collections package (Version 1.0 or later)</li>
+<li><a href="http://www.junit.org">JUnit Testing Framework</a>
+    (Version 3.2 or later) - for unit tests only, not required
+    for deployment</li>
+</ul>
+
+
+<a name="Release Info"></a>
+<h3>3.  RELEASE INFO</h3>
+
+<p>Current Release:  <strong>Unreleased, CVS Repository Only</strong></p>
+
+<p>Planned Next Release:  TBD</p>
+
+<a name="Committers"></a>
+<h3>4.  COMMITTERS</h3>
+
+<p>The following individuals are the primary developers and maintainers of this
+component.  Developers who plan to use the <em>Pool</em> package in their own
+projects are encouraged to collaborate on the future development of this
+component to ensure that it continues to meet a variety of needs.</p>
+
+<ul>
+   <li><a href="mailto:morgand@apache.org">Morgan Delagrange</a></li>
+   <li><a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a></li>
+   <li><a href="mailto:craigmcc@apache.org">Craig McClanahan</a></li>
+   <li><a href="mailto:rwaldhoff@apache.org">Rodney Waldhoff</a></li>
+   <li><a href="mailto:dweinr1@apache.org">David Weinrich</a></li>
+</ul>
+
+<a name="Action Items"></a>
+<h3>5.  ACTION ITEMS</h3>
+
+<p>The following action items need to be completed prior to a Version 1.0
+release of this component:</p>
+
+<table border="1">
+  <tr>
+    <th width="80%">Action Item</th>
+    <th width="20%">Volunteer</th>
+  </tr>
+
+  <tr>
+    <td><strong>Weak-Reference Based Pools</strong> - pool implementations
+    that keep weak references to the pooled instances, evicting objects
+    when the VM needs memory</td>
+    <td align="center">&nbsp;</td>
+  </tr>
+
+  <tr>
+    <td><strong>Install / Use Documentation</strong>.  Expanded examples and
+    installation documentation. </td>
+    <td align="center">&nbsp;</td>
+  </tr>
+
+</table>
+
+</body>
+</html>
diff --git a/build.properties.sample b/build.properties.sample
new file mode 100644
index 0000000..581170e
--- /dev/null
+++ b/build.properties.sample
@@ -0,0 +1,4 @@
+# commons-collections.jar - Commons-Collections Classpath
+commons-collections.jar=${basedir}/../collections/build/commons-collections.jar
+# junit.jar - JUnit 3.2+ Classpath
+junit.jar=/java/junit/junit.jar
\ No newline at end of file
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..99beb0c
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,225 @@
+<!-- $Id: build.xml,v 1.1 2001/04/14 16:40:37 rwaldhoff Exp $ -->
+<project name="commons-pool" default="test" basedir=".">
+
+   <!-- patternset describing files to be copied from the doc directory -->
+   <patternset id="patternset-doc"/>
+
+   <!-- patternset describing test classes -->
+   <patternset id="patternset-test-classes">
+      <include name="**/Test*.class"/>
+   </patternset>
+
+   <!-- patternset describing non test classes -->
+   <patternset id="patternset-non-test-classes">
+      <include name="**/*.class"/>
+      <exclude name="**/Test*.class"/>
+   </patternset>
+
+   <!-- patternset describing non test source files (*.java, *html, etc.) -->
+   <patternset id="patternset-javadocable-sources">
+      <include name="**/*"/>
+      <exclude name="**/Test*.java"/>
+   </patternset>
+
+   <!-- ######################################################### -->
+
+   <target name="init">
+      <tstamp/>
+
+      <!-- read properties from the ${user.home}/propfile, if any -->
+      <property name="user-propfile" value="${user.home}/build.properties"/>
+      <property file="${user-propfile}"/>
+      <property name="user-classpath" value=""/>
+      <!-- read properties from the build.properties, if any -->
+      <property file="${basedir}/build.properties"/>
+
+      <!-- command line classpath, if any -->
+      <property name="cp" value=""/>
+
+      <!-- now combine the classpaths -->
+      <property name="classpath" value="${cp}:${user-classpath}:${commons-collections.jar}:${junit.jar}"/>
+
+      <property name="name" value="commons-pool"/>
+      <property name="Name" value="Commons-Pool"/>
+      <property name="Name-Long" value="Jakarta Commons Object Pooling Package"/>
+
+      <property name="test.entry" value="org.apache.commons.pool.TestAll"/>
+      <property name="test.failonerror" value="true" /> 
+      <property name="test.runner" value="junit.textui.TestRunner" /> 
+      
+      <property name="workdir" value="${java.io.tmpdir}/buildtemp_${DSTAMP}${TSTAMP}"/>
+      <property name="source" value="${basedir}"/>
+      <property name="source.src" value="${basedir}/src"/>
+      <property name="source.src.java" value="${source.src}/java"/>
+      <property name="source.src.test" value="${source.src}/test"/>
+      <property name="source.doc" value="${basedir}/doc"/>
+      <property name="dest" value="${basedir}/build"/>
+      <property name="dest.classes" value="${dest}/classes"/>
+      <property name="dest.doc" value="${dest}/doc"/>
+      <property name="dest.doc.api" value="${dest.doc}/api"/>
+      <property name="dest.jardir" value="${dest}"/>
+      <property name="dest.jardir.jar" value="${dest.jardir}/${name}.jar"/>
+
+      <available property="available-doc" file="${source.doc}"/> <!-- does this module have docs? -->
+      <available property="available-src-java" file="${source.src.java}"/> <!-- does this module have java src? -->      
+      <available property="available-src-test" file="${source.src.test}"/> <!-- does this module have test src? -->      
+
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="copy-javadoc-source" depends="init" if="available-src-java">
+      <mkdir dir="${javadoc-source-dir}"/>
+      <copy todir="${javadoc-source-dir}" filtering="no">
+         <fileset dir="${source.src.java}">
+            <patternset refid="patternset-javadocable-sources"/>
+         </fileset>
+      </copy>
+   </target>
+
+   <target name="copy-doc" depends="init" if="available-doc">
+      <mkdir dir="${doc-source-dir}/${name}"/>
+      <copy todir="${doc-source-dir}/${name}" filtering="no">
+         <fileset dir="${source.doc}">
+            <patternset refid="patternset-doc"/>
+         </fileset>
+      </copy>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="clean" depends="init,clean-doc,clean-build,clean-dist" description="removes generated files">
+      <delete dir="${dest}"/>
+   </target>
+
+   <target name="clean-doc" depends="init,clean-javadoc">
+      <delete dir="${dest.doc}"/>
+   </target>
+
+   <target name="clean-javadoc" depends="init">
+      <delete dir="${dest.doc.api}"/>
+   </target>
+
+   <target name="clean-build" depends="init">
+      <delete dir="${dest.classes}"/>
+   </target>
+
+   <target name="clean-dist" depends="init">
+      <delete file="${dest.jardir.jar}"/>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="doc" depends="init,doc-copy,doc-javadoc" description="generates javadocs and other documentation">
+   </target>
+
+   <target name="doc-copy" depends="init" if="available-doc">
+      <mkdir dir="${dest.doc}"/>
+      <copy todir="${dest.doc}">
+      <fileset dir="${source.doc}">
+         <patternset refid="patternset-doc"/>
+      </fileset>
+      </copy>
+   </target>
+
+   <target name="doc-javadoc" depends="init" if="available-src-java">
+      <!-- copy all the non-test sources out to the work directory and javadoc that -->
+      <mkdir dir="${workdir}"/>
+      <copy todir="${workdir}">
+        <fileset dir="${source.src.java}">
+          <patternset refid="patternset-javadocable-sources"/>
+        </fileset>
+      </copy>
+      <mkdir dir="${dest.doc.api}"/>
+      <javadoc packagenames="org.*"
+               sourcepath="${workdir}"
+               classpath="${classpath}"
+               destdir="${dest.doc.api}"
+               windowtitle="${Name-Long}"
+               doctitle="${Name-Long}"
+               bottom="&lt;small&gt;Copyright &amp;copy; 2001 Apache Software Foundation. Documenation generated ${TODAY}&lt;/small&gt;."
+               public="true"
+               version="true"
+               author="false"
+               splitindex="false"
+               nodeprecated="true"
+               nodeprecatedlist="true"
+               notree="true"
+               noindex="false"
+               nohelp="true"
+               nonavbar="false"
+               serialwarn="false">
+      </javadoc>
+      <delete dir="${workdir}"/>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="build" depends="init,build-java" description="compiles source files"/>
+
+   <target name="build-java" depends="init" if="available-src-java">
+      <mkdir dir="${dest.classes}"/>
+      <javac destdir="${dest.classes}"
+             srcdir="${source.src.java}"
+             classpath="${classpath}"
+             debug="false"
+             deprecation="true"
+             optimize="true"/>
+   </target>
+
+   <target name="build-test" depends="init,build-java" if="available-src-test">
+      <mkdir dir="${dest.classes}"/>
+      <javac destdir="${dest.classes}"
+             srcdir="${source.src.test}"
+             classpath="${classpath}"
+             debug="false"
+             deprecation="true"
+             optimize="true"/>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="test" depends="build-test" if="test.entry" description="runs (junit) unit tests">
+      <!--
+      <junit printsummary="yes" fork="on" haltonfailure="yes">
+      	<formatter type="plain" usefile="false"/>
+      	<test name="${test.entry}"/>
+      	<classpath>
+      		<pathelement location="${dest.classes}" />
+      		<pathelement path="${classpath}" />
+      		<pathelement path="${java.class.path}" />
+      	</classpath>
+      </junit>
+      -->
+      
+      <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}">
+        <arg value="${test.entry}"/> 
+        <classpath>
+          <pathelement location="${dest.classes}" /> 
+          <pathelement path="${classpath}" /> 
+          <pathelement path="${java.class.path}" /> 
+        </classpath>
+      </java>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="dist" depends="dist-jar" description="builds production jar"/>
+
+   <target name="dist-jar" depends="build">
+      <mkdir dir="${dest.jardir}"/>
+      <mkdir dir="${workdir}"/>
+      <copy todir="${workdir}">
+         <fileset dir="${dest.classes}">
+            <patternset refid="patternset-non-test-classes"/>
+         </fileset>
+      </copy>
+      <jar jarfile="${dest.jardir.jar}">
+         <fileset dir="${workdir}"/>
+      </jar>
+      <delete dir="${workdir}"/>
+   </target>
+
+   <!-- ######################################################### -->
+
+</project>
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/BaseKeyedPoolableObjectFactory.java b/src/java/org/apache/commons/pool/BaseKeyedPoolableObjectFactory.java
new file mode 100644
index 0000000..04e1948
--- /dev/null
+++ b/src/java/org/apache/commons/pool/BaseKeyedPoolableObjectFactory.java
@@ -0,0 +1,97 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A base implemenation of {@link KeyedPoolableObjectFactory <tt>KeyedPoolableObjectFactory</tt>}.
+ * <p>
+ * All operations defined here are essentially no-op's.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: BaseKeyedPoolableObjectFactory.java,v 1.1 2001/04/14 16:40:38 rwaldhoff Exp $
+ *
+ * @see KeyedPoolableObjectFactory
+ * @see BasePoolableObjectFactory
+ */
+public abstract class BaseKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory {
+    public abstract Object makeObject(Object key);
+
+    /**
+     * No-op.
+     */
+    public void destroyObject(Object key, Object obj) {
+    }
+
+    /**
+     * This implementation always returns <tt>true</tt>.
+     * @return <tt>true</tt>
+     */
+    public boolean validateObject(Object key, Object obj) {
+        return true;
+    }
+
+    /**
+     * No-op.
+     */
+    public void activateObject(Object key, Object obj) {
+    }
+
+    /**
+     * No-op.
+     */
+    public void passivateObject(Object key, Object obj) {
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/BasePoolableObjectFactory.java b/src/java/org/apache/commons/pool/BasePoolableObjectFactory.java
new file mode 100644
index 0000000..76e4cd4
--- /dev/null
+++ b/src/java/org/apache/commons/pool/BasePoolableObjectFactory.java
@@ -0,0 +1,103 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/BasePoolableObjectFactory.java,v 1.1 2001/04/14 16:40:40 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:40 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A base implemenation of {@link PoolableObjectFactory <tt>PoolableObjectFactory</tt>}.
+ * <p>
+ * All operations defined here are essentially no-op's.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: BasePoolableObjectFactory.java,v 1.1 2001/04/14 16:40:40 rwaldhoff Exp $
+ *
+ * @see PoolableObjectFactory
+ * @see BaseKeyedPoolableObjectFactory
+ */
+public abstract class BasePoolableObjectFactory implements PoolableObjectFactory {
+    public abstract Object makeObject();
+
+    /**
+     * No-op.
+     */
+    public void destroyObject(Object obj) {
+    }
+
+    /**
+     * This implementation always returns <tt>true</tt>.
+     * @return <tt>true</tt>
+     */
+    public boolean validateObject(Object obj) {
+        return true;
+    }
+
+    /**
+     * No-op.
+     */
+    public void activateObject(Object obj) {
+    }
+
+    /**
+     * No-op.
+     */
+    public void passivateObject(Object obj) {
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/KeyedObjectPool.java b/src/java/org/apache/commons/pool/KeyedObjectPool.java
new file mode 100644
index 0000000..12ab22b
--- /dev/null
+++ b/src/java/org/apache/commons/pool/KeyedObjectPool.java
@@ -0,0 +1,201 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/KeyedObjectPool.java,v 1.1 2001/04/14 16:40:43 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:43 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple "keyed" {@link Object} pooling interface.
+ * <p>
+ * A keyed pool caches mutiple named instances of {@link Object}s.
+ * <p>
+ * Example of use:
+ * <table border="1" cellspacing="0" cellpadding="3" align="center" bgcolor="#FFFFFF"><tr><td><pre>
+ * Object obj = <font color="#0000CC">null</font>;
+ * Object key = <font color="#CC0000">"Key"</font>;
+ * <font color="#0000CC">try</font> {
+ *    obj = keyedPool.borrowObject(key);
+ *    <font color="#00CC00">//...use the object...</font>
+ * } <font color="#0000CC">catch</font>(Exception e) {
+ *    <font color="#00CC00">//...handle any exceptions...</font>
+ * } <font color="#0000CC">finally</font> {
+ *    <font color="#00CC00">// make sure the object is returned to the pool</font>
+ *    <font color="#0000CC">if</font>(<font color="#0000CC">null</font> != obj) {
+ *       keyedPool.returnObject(key,obj);
+ *    }
+ * }</pre></td></tr></table>
+ *
+ * <p>
+ * {@link KeyedObjectPool} implementations <i>may</i> choose to store at most
+ * one instance per key value, or may choose to maintain a pool of instances
+ * for each key (essentially creating a Map of Pools).
+ * </p>
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: KeyedObjectPool.java,v 1.1 2001/04/14 16:40:43 rwaldhoff Exp $
+ *
+ * @see KeyedPoolableObjectFactory
+ * @see KeyedObjectPoolFactory
+ * @see ObjectPool
+ */
+public interface KeyedObjectPool {
+    /**
+     * Obtain an <tt>Object</tt> from my pool
+     * using the specified <i>key</i>.
+     * By contract, clients MUST return
+     * the borrowed object using
+     * {@link #returnObject(java.lang.Object,java.lang.Object) <tt>returnObject</tt>},
+     * or a related method as defined in an implementation
+     * or sub-interface,
+     * using a <i>key</i> that is equivalent to the one used to
+     * borrow the <tt>Object</tt> in the first place.
+     *
+     * @param key the key used to obtain the object
+     * @return an <tt>Object</tt> from my pool.
+     */
+    public abstract Object borrowObject(Object key);
+
+    /**
+     * Return an <tt>Object</tt> to my pool.
+     * By contract, <i>obj</i> MUST have been obtained
+     * using {@link #borrowObject(java.lang.Object) <tt>borrowObject</tt>}
+     * or a related method as defined in an implementation
+     * or sub-interface,
+     * using a <i>key</i> that is equivalent to the one used to
+     * borrow the <tt>Object</tt> in the first place.
+     *
+     * @param key the key used to obtain the object
+     * @param obj a {@link #borrowObject(java.lang.Object) borrowed} <tt>Object</tt> to be returned.
+     */
+    public abstract void returnObject(Object key, Object obj);
+
+    /**
+     * Return the number of <tt>Object</tt>s
+     * corresponding to the given <i>key</i>
+     * currently idle in my pool, or
+     * throws {@link UnsupportedOperationException}
+     * if this information is not available.
+     *
+     * @param key the key
+     * @return the number of <tt>Object</tt>s corresponding to the given <i>key</i> currently idle in my pool
+     * @throws UnsupportedOperationException
+     */
+    public abstract int numIdle(Object key) throws UnsupportedOperationException;
+
+    /**
+     * Return the number of <tt>Object</tt>s
+     * currently borrowed from my pool corresponding to the
+     * given <i>key</i>, or
+     * throws {@link UnsupportedOperationException}
+     * if this information is not available.
+     *
+     * @param key the key
+     * @return the number of <tt>Object</tt>s corresponding to the given <i>key</i> currently borrowed in my pool
+     * @throws UnsupportedOperationException
+     */
+    public abstract int numActive(Object key) throws UnsupportedOperationException;
+
+    /**
+     * Return the total number of <tt>Object</tt>s
+     * currently idle in my pool, or
+     * throws {@link UnsupportedOperationException}
+     * if this information is not available.
+     *
+     * @return the total number of <tt>Object</tt>s currently idle in my pool
+     * @throws UnsupportedOperationException
+     */
+    public abstract int numIdle() throws UnsupportedOperationException;
+
+    /**
+     * Return the total number of <tt>Object</tt>s
+     * current borrowed from my pool, or
+     * throws {@link UnsupportedOperationException}
+     * if this information is not available.
+     *
+     * @return the total number of <tt>Object</tt>s currently borrowed in my pool
+     * @throws UnsupportedOperationException
+     */
+    public abstract int numActive() throws UnsupportedOperationException;
+
+    /**
+     * Clears my pool, or throws {@link UnsupportedOperationException}
+     * if the pool cannot be cleared.
+     */
+    public abstract void clear() throws UnsupportedOperationException;
+
+    /**
+     * Clears the specified pool, or throws {@link UnsupportedOperationException}
+     * if the pool cannot be cleared.
+     * @param key the key to clear
+     */
+    public abstract void clear(Object key) throws UnsupportedOperationException;
+
+    /**
+     * Close this pool, and free any resources associated with it.
+     */
+    public abstract void close();
+
+    /**
+     * Sets the {@link KeyedPoolableObjectFactory factory} I use
+     * to create new instances.
+     * @param factory the {@link KeyedPoolableObjectFactory} I use to create new instances.
+     */
+    public abstract void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException;
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/KeyedObjectPoolFactory.java b/src/java/org/apache/commons/pool/KeyedObjectPoolFactory.java
new file mode 100644
index 0000000..b81de67
--- /dev/null
+++ b/src/java/org/apache/commons/pool/KeyedObjectPoolFactory.java
@@ -0,0 +1,78 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/KeyedObjectPoolFactory.java,v 1.1 2001/04/14 16:40:45 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:45 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple factory interface for creating {@link KeyedObjectPool}s.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: KeyedObjectPoolFactory.java,v 1.1 2001/04/14 16:40:45 rwaldhoff Exp $
+ *
+ * @see KeyedObjectPool
+ */
+public interface KeyedObjectPoolFactory {
+    /**
+     * Create and return a new {@link KeyedObjectPool}.
+     * @return a new, empty {@link KeyedObjectPool}
+     */
+    public abstract KeyedObjectPool createPool();
+}
diff --git a/src/java/org/apache/commons/pool/KeyedPoolableObjectFactory.java b/src/java/org/apache/commons/pool/KeyedPoolableObjectFactory.java
new file mode 100644
index 0000000..0ef34e2
--- /dev/null
+++ b/src/java/org/apache/commons/pool/KeyedPoolableObjectFactory.java
@@ -0,0 +1,145 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/KeyedPoolableObjectFactory.java,v 1.1 2001/04/14 16:40:47 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:47 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple interface defining life-cycle methods for
+ * <tt>Object</tt>s to be used in a
+ * {@link KeyedObjectPool <tt>KeyedObjectPool</tt>}.
+ * <p>
+ * By contract, when an {@link KeyedObjectPool <tt>KeyedObjectPool</tt>}
+ * delegates to a <tt>KeyedPoolableObjectFactory</tt>,
+ * <ol>
+ *  <li>
+ *   {@link #makeObject(java.lang.Object) <tt>makeObject</tt>} is called whenever a new <tt>Object</tt>
+ *   is needed.
+ *  </li>
+ *  <li>
+ *   {@link #activateObject(java.lang.Object,java.lang.Object) <tt>activateObject</tt>} is invoked
+ *   on every <tt>Object</tt> before it is returned from the
+ *   {@link KeyedObjectPool <tt>KeyedObjectPool</tt>}.
+ *  </li>
+ *  <li>
+ *   {@link #passivateObject(java.lang.Object,java.lang.Object) <tt>passivateObject</tt>} is invoked
+ *   on every <tt>Object</tt> when it is returned to the
+ *   {@link KeyedObjectPool <tt>KeyedObjectPool</tt>}.
+ *  </li>
+ *  <li>
+ *   {@link #destroyObject(java.lang.Object,java.lang.Object) <tt>destroyObject</tt>} is invoked
+ *   on every <tt>Object</tt> when it is being "dropped" from the
+ *   {@link KeyedObjectPool <tt>KeyedObjectPool</tt>} (whether due to the response from
+ *   {@link #validateObject(java.lang.Object,java.lang.Object) <tt>validateObject</tt>}, or
+ *   for reasons specific to the {@link KeyedObjectPool <tt>KeyedObjectPool</tt>} implementation.)
+ *  </li>
+ *  <li>
+ *   {@link #validateObject(java.lang.Object,java.lang.Object) <tt>validateObject</tt>} is invoked
+ *   in an implementation-specific fashion to determine if an <tt>Object</tt>
+ *   is still valid to be returned by the{@link KeyedObjectPool <tt>KeyedObjectPool</tt>}.
+ *   It will only be invoked on an {@link #activateObject(java.lang.Object,java.lang.Object) "activated"}
+ *   <tt>Object</tt>.
+ *  </li>
+ * </ol>
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: KeyedPoolableObjectFactory.java,v 1.1 2001/04/14 16:40:47 rwaldhoff Exp $
+ *
+ * @see KeyedObjectPool
+ */
+public interface KeyedPoolableObjectFactory {
+    /**
+     * Create an Object that can be returned by the pool.
+     * @param key the key used when constructing the object
+     * @return an Object that can be returned by the pool.
+     */
+    public abstract Object makeObject(Object key);
+
+    /**
+     * Destroy an Object no longer needed by the pool.
+     * @param key the key used when selecting the object
+     * @param obj the Object to be destroyed
+     */
+    public abstract void destroyObject(Object key, Object obj);
+
+    /**
+     * Ensures that the Object is safe to be returned by the pool.
+     * Returns <tt>false</tt> if this object should be destroyed.
+     * @param key the key used when selecting the object
+     * @param obj the <tt>Object</tt> to be validated
+     * @return <tt>false</tt> if this <i>obj</i> is not valid and should
+     *         be dropped from the pool, <tt>true</tt> otherwise.
+     */
+    public abstract boolean validateObject(Object key, Object obj);
+
+    /**
+     * Reinitialize an Object to be returned by the pool.
+     * @param key the key used when selecting the object
+     * @param obj the <tt>Object</tt> to be activated
+     */
+    public abstract void activateObject(Object key, Object obj);
+
+    /**
+     * Uninitialize an Object to be returned to the pool.
+     * @param key the key used when selecting the object
+     * @param obj the <tt>Object</tt> to be passivated
+     */
+    public abstract void passivateObject(Object key, Object obj);
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/ObjectPool.java b/src/java/org/apache/commons/pool/ObjectPool.java
new file mode 100644
index 0000000..4558aa7
--- /dev/null
+++ b/src/java/org/apache/commons/pool/ObjectPool.java
@@ -0,0 +1,158 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/ObjectPool.java,v 1.1 2001/04/14 16:40:49 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:49 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple {@link Object} pooling interface.
+ * <p>
+ * Example of use:
+ * <table border="1" cellspacing="0" cellpadding="3" align="center" bgcolor="#FFFFFF"><tr><td><pre>
+ * Object obj = <font color="#0000CC">null</font>;
+ * <font color="#0000CC">try</font> {
+ *    obj = pool.borrowObject();
+ *    <font color="#00CC00">//...use the object...</font>
+ * } <font color="#0000CC">catch</font>(Exception e) {
+ *    <font color="#00CC00">//...handle any exceptions...</font>
+ * } <font color="#0000CC">finally</font> {
+ *    <font color="#00CC00">// make sure the object is returned to the pool</font>
+ *    <font color="#0000CC">if</font>(<font color="#0000CC">null</font> != obj) {
+ *       pool.returnObject(obj);
+ *    }
+ * }</pre></td></tr></table>
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: ObjectPool.java,v 1.1 2001/04/14 16:40:49 rwaldhoff Exp $
+ *
+ * @see KeyedObjectPool
+ * @see ObjectPoolFactory
+ * @see PoolableObjectFactory
+ */
+public interface ObjectPool {
+    /**
+     * Obtain an <tt>Object</tt> from my pool.
+     * By contract, clients MUST return
+     * the borrowed object using
+     * {@link #returnObject(java.lang.Object) <tt>returnObject</tt>}
+     * or a related method as defined in an implementation
+     * or sub-interface.
+     * <p>
+     * The behaviour of this method when the pool has been exhausted
+     * is not specified (although it may be specified by implementations).
+     *
+     * @return an <tt>Object</tt> from my pool.
+     */
+    public abstract Object borrowObject();
+
+    /**
+     * Return an <tt>Object</tt> to my pool.
+     * By contract, <i>obj</i> MUST have been obtained
+     * using {@link #borrowObject() <tt>borrowObject</tt>}
+     * or a related method as defined in an implementation
+     * or sub-interface.
+     *
+     * @param obj a {@link #borrowObject() borrowed} <tt>Object</tt> to be returned.
+     */
+    public abstract void returnObject(Object obj);
+
+    /**
+     * Return the number of <tt>Object</tt>s
+     * currently idle in my pool, or
+     * throws {@link UnsupportedOperationException}
+     * if this information is not available.
+     *
+     * @return the number of <tt>Object</tt>s currently idle in my pool
+     * @throws UnsupportedOperationException
+     */
+    public abstract int numIdle() throws UnsupportedOperationException;
+
+    /**
+     * Return the number of <tt>Object</tt>s
+     * currently borrowed from my pool, or
+     * throws {@link UnsupportedOperationException}
+     * if this information is not available.
+     *
+     * @return the number of <tt>Object</tt>s currently borrowed in my pool
+     * @throws UnsupportedOperationException
+     */
+    public abstract int numActive() throws UnsupportedOperationException;
+
+    /**
+     * Clears any objects sitting idle in the pool, releasing any
+     * associated resources, or throws {@link UnsupportedOperationException}
+     * if the pool cannot be cleared.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public abstract void clear() throws UnsupportedOperationException;
+
+    /**
+     * Close this pool, and free any resources associated with it.
+     */
+    public abstract void close();
+
+    /**
+     * Sets the {@link PoolableObjectFactory factory} I use
+     * to create new instances.
+     * @param factory the {@link PoolableObjectFactory} I use to create new instances.
+     */
+    public abstract void setFactory(PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException;
+}
diff --git a/src/java/org/apache/commons/pool/ObjectPoolFactory.java b/src/java/org/apache/commons/pool/ObjectPoolFactory.java
new file mode 100644
index 0000000..2afaad6
--- /dev/null
+++ b/src/java/org/apache/commons/pool/ObjectPoolFactory.java
@@ -0,0 +1,78 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/ObjectPoolFactory.java,v 1.1 2001/04/14 16:40:50 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:50 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple factory interface for creating {@link ObjectPool}s.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: ObjectPoolFactory.java,v 1.1 2001/04/14 16:40:50 rwaldhoff Exp $
+ *
+ * @see ObjectPool
+ */
+public interface ObjectPoolFactory {
+    /**
+     * Create and return a new {@link ObjectPool}.
+     * @return a new, empty {@link ObjectPool}
+     */
+    public abstract ObjectPool createPool();
+}
diff --git a/src/java/org/apache/commons/pool/PoolableObjectFactory.java b/src/java/org/apache/commons/pool/PoolableObjectFactory.java
new file mode 100644
index 0000000..9e7ca95
--- /dev/null
+++ b/src/java/org/apache/commons/pool/PoolableObjectFactory.java
@@ -0,0 +1,141 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/PoolableObjectFactory.java,v 1.1 2001/04/14 16:40:53 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:40:53 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple interface defining life-cycle methods for
+ * <tt>Object</tt>s to be used in an
+ * {@link ObjectPool <tt>ObjectPool</tt>}.
+ * <p>
+ * By contract, when an {@link ObjectPool <tt>ObjectPool</tt>}
+ * delegates to a <tt>PoolableObjectFactory</tt>,
+ * <ol>
+ *  <li>
+ *   {@link #makeObject <tt>makeObject</tt>} is called whenever a new <tt>Object</tt>
+ *   is needed.
+ *  </li>
+ *  <li>
+ *   {@link #activateObject(java.lang.Object) <tt>activateObject</tt>} is invoked
+ *   on every <tt>Object</tt> before it is returned from the
+ *   {@link ObjectPool}, and before it is {@link #validateObject validated}.
+ *  </li>
+ *  <li>
+ *   {@link #passivateObject(java.lang.Object) <tt>passivateObject</tt>} is invoked
+ *   on every <tt>Object</tt> when it is returned to the
+ *   {@link ObjectPool <tt>ObjectPool</tt>}.
+ *  </li>
+ *  <li>
+ *   {@link #destroyObject(java.lang.Object) <tt>destroyObject</tt>} is invoked
+ *   on every <tt>Object</tt> when it is being "dropped" from the
+ *   {@link ObjectPool <tt>ObjectPool</tt>} (whether due to the response from
+ *   {@link #validateObject(java.lang.Object) <tt>validateObject</tt>}, or
+ *   for reasons specific to the {@link ObjectPool <tt>ObjectPool</tt>} implementation.)
+ *  </li>
+ *  <li>
+ *   {@link #validateObject(java.lang.Object) <tt>validateObject</tt>} is invoked
+ *   in an implementation-specific fashion to determine if an <tt>Object</tt>
+ *   is still valid to be returned by the {@link ObjectPool <tt>ObjectPool</tt>}.
+ *   It will only be invoked on an {@link #activateObject(java.lang.Object) "activated"}
+ *   <tt>Object</tt>.
+ *  </li>
+ * </ol>
+ *
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: PoolableObjectFactory.java,v 1.1 2001/04/14 16:40:53 rwaldhoff Exp $
+ *
+ * @see ObjectPool
+ */
+public interface PoolableObjectFactory {
+  /**
+   * Creates an Object that can be returned by the pool.
+   * @return an Object that can be returned by the pool.
+   */
+  public abstract Object makeObject();
+
+  /**
+   * Destroys an Object no longer needed by the pool.
+   * @param obj the <tt>Object</tt> to be destroyed
+   */
+  public abstract void destroyObject(Object obj);
+
+  /**
+   * Ensures that the Object is safe to be returned by the pool.
+   * Returns <tt>false</tt> if this object should be destroyed.
+   * @param obj the <tt>Object</tt> to be validated
+   * @return <tt>false</tt> if this <i>obj</i> is not valid and should
+   *         be dropped from the pool, <tt>true</tt> otherwise.
+   */
+  public abstract boolean validateObject(Object obj);
+
+  /**
+   * Reinitialize an Object to be returned by the pool.
+   * @param obj the <tt>Object</tt> to be activated
+   */
+  public abstract void activateObject(Object obj);
+
+  /**
+   * Uninitialize an Object to be returned to the pool.
+   * @param obj the <tt>Object</tt> to be passivated
+   */
+  public abstract void passivateObject(Object obj);
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java b/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
new file mode 100644
index 0000000..2e3cd60
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
@@ -0,0 +1,1282 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java,v 1.1 2001/04/14 16:41:21 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:21 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+import org.apache.commons.collections.CursorableLinkedList;
+import org.apache.commons.collections.CursorableLinkedList.Cursor;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+import java.util.EmptyStackException;
+import java.util.ListIterator;
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * A configurable {@link KeyedObjectPool} implementation.
+ * <p>
+ * When coupled with the appropriate {@link KeyedPoolableObjectFactory},
+ * <tt>GenericKeyedObjectPool</tt> provides robust pooling functionality for
+ * arbitrary objects.
+ * <p>
+ * A <tt>GenericKeyedObjectPool</tt> provides a number of configurable parameters:
+ * <ul>
+ *  <li>
+ *    {@link #setMaxActive <i>maxActive</i>} controls the maximum number of objects (per key)
+ *    that can be borrowed from the pool at one time.  When non-positive, there
+ *    is no limit to the number of objects that may be active at one time.
+ *    When {@link #setMaxActive <i>maxActive</i>} is exceeded, the pool is said to be exhausted.
+ *  </li>
+ *  <li>
+ *    {@link #setMaxIdle <i>maxIdle</i>} controls the maximum number of objects that can
+ *    sit idle in the pool (per key) at any time.  When non-positive, there
+ *    is no limit to the number of objects that may be idle at one time.
+ *  </li>
+ *  <li>
+ *    {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} specifies the
+ *    behaviour of the {@link #borrowObject} method when the pool is exhausted:
+ *    <ul>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} is
+ *      {@link #WHEN_EXHAUSTED_FAIL}, {@link #borrowObject} will throw
+ *      a {@link NoSuchElementException}
+ *    </li>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} is
+ *      {@link #WHEN_EXHAUSTED_GROW}, {@link #borrowObject} will create a new
+ *      object and return it(essentially making {@link #setMaxActive <i>maxActive</i>}
+ *      meaningless.)
+ *    </li>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>}
+ *      is {@link #WHEN_EXHAUSTED_BLOCK}, {@link #borrowObject} will block
+ *      (invoke {@link Object#wait} until a new or idle object is available.
+ *      If a positive {@link #setMaxWait <i>maxWait</i>}
+ *      value is supplied, the {@link #borrowObject} will block for at
+ *      most that many milliseconds, after which a {@link NoSuchElementException}
+ *      will be thrown.  If {@link #setMaxWait <i>maxWait</i>} is non-positive,
+ *      the {@link #borrowObject} method will block indefinitely.
+ *    </li>
+ *    </ul>
+ *  </li>
+ *  <li>
+ *    When {@link #setTestOnBorrow <i>testOnBorrow</i>} is set, the pool will
+ *    attempt to validate each object before it is returned from the
+ *    {@link #borrowObject} method. (Using the provided factory's
+ *    {@link PoolableObjectFactory#validateObject} method.)  Objects that fail
+ *    to validate will be dropped from the pool, and a different object will
+ *    be borrowed.
+ *  </li>
+ *  <li>
+ *    When {@link #setTestOnReturn <i>testOnReturn</i>} is set, the pool will
+ *    attempt to validate each object before it is returned to the pool in the
+ *    {@link #returnObject} method. (Using the provided factory's
+ *    {@link PoolableObjectFactory#validateObject}
+ *    method.)  Objects that fail to validate will be dropped from the pool.
+ *  </li>
+ * </ul>
+ * <p>
+ * Optionally, one may configure the pool to examine and possibly evict objects as they
+ * sit idle in the pool.  This is performed by an "idle object eviction" thread, which
+ * runs asychronously.  The idle object eviction thread may be configured using the
+ * following attributes:
+ * <ul>
+ *  <li>
+ *   {@link #setTimeBetweenEvictionRunsMillis <i>timeBetweenEvictionRunsMillis</i>}
+ *   indicates how long the eviction thread should sleep before "runs" of examining
+ *   idle objects.  When non-positive, no eviction thread will be launched.
+ *  </li>
+ *  <li>
+ *   {@link #setMinEvictableIdleTimeMillis <i>minEvictableIdleTimeMillis</i>}
+ *   specifies the minimum amount of time that an object may sit idle in the pool
+ *   before it is eligable for eviction due to idle time.  When non-positive, no object
+ *   will be dropped from the pool due to idle time alone.
+ *  </li>
+ *  <li>
+ *   {@link #setTestWhileIdle <i>testWhileIdle</i>} indicates whether or not idle
+ *   objects should be validated using the factory's
+ *   {@link PoolableObjectFactory#validateObject} method.  Objects
+ *   that fail to validate will be dropped from the pool.
+ *  </li>
+ * </ul>
+ * @see GenericObjectPool
+ * @author Rodney Waldhoff
+ * @version $Id: GenericKeyedObjectPool.java,v 1.1 2001/04/14 16:41:21 rwaldhoff Exp $
+ */
+public class GenericKeyedObjectPool implements KeyedObjectPool {
+
+    //--- public constants -------------------------------------------
+
+    /**
+     * A "when exhausted action" type indicating that when the pool is
+     * exhausted (i.e., the maximum number of active objects has
+     * been reached), the {@link #borrowObject}
+     * method should fail, throwing a {@link NoSuchElementException}.
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte WHEN_EXHAUSTED_FAIL   = 0;
+
+    /**
+     * A "when exhausted action" type indicating that when the pool
+     * is exhausted (i.e., the maximum number
+     * of active objects has been reached), the {@link #borrowObject}
+     * method should block until a new object is available, or the
+     * {@link #getMaxWait maximum wait time} has been reached.
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setMaxWait
+     * @see #getMaxWait
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte WHEN_EXHAUSTED_BLOCK  = 1;
+
+    /**
+     * A "when exhausted action" type indicating that when the pool is
+     * exhausted (i.e., the maximum number
+     * of active objects has been reached), the {@link #borrowObject}
+     * method should simply create a new object anyway.
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte WHEN_EXHAUSTED_GROW   = 2;
+
+    /**
+     * The default cap on the number of idle instances in the pool
+     * (per key).
+     * @see #getMaxIdle
+     * @see #setMaxIdle
+     */
+    public static final int DEFAULT_MAX_IDLE  = 8;
+
+    /**
+     * The default cap on the total number of active instances from the pool
+     * (per key).
+     * @see #getMaxActive
+     * @see #setMaxActive
+     */
+    public static final int DEFAULT_MAX_ACTIVE  = 8;
+
+    /**
+     * The default "when exhausted action" for the pool.
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte DEFAULT_WHEN_EXHAUSTED_ACTION = WHEN_EXHAUSTED_BLOCK;
+
+    /**
+     * The default maximum amount of time (in millis) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #getWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     * @see #getMaxWait
+     * @see #setMaxWait
+     */
+    public static final long DEFAULT_MAX_WAIT = -1L;
+
+    /**
+     * The default "test on borrow" value.
+     * @see #getTestOnBorrow
+     * @see #setTestOnBorrow
+     */
+    public static final boolean DEFAULT_TEST_ON_BORROW = false;
+
+    /**
+     * The default "test on return" value.
+     * @see #getTestOnReturn
+     * @see #setTestOnReturn
+     */
+    public static final boolean DEFAULT_TEST_ON_RETURN = false;
+
+    /**
+     * The default "test while idle" value.
+     * @see #getTestWhileIdle
+     * @see #setTestWhileIdle
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public static final boolean DEFAULT_TEST_WHILE_IDLE = false;
+
+    /**
+     * The default "time between eviction runs" value.
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L;
+
+    /**
+     * The default number of objects to examine per run in the
+     * idle object evictor.
+     * @see #getNumTestsPerEvictionRun
+     * @see #setNumTestsPerEvictionRun
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3;
+
+    /**
+     * The default value for {@link #getMinEvictableIdleTimeMillis}.
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #setMinEvictableIdleTimeMillis
+     */
+    public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L;
+
+    //--- constructors -----------------------------------------------
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory) {
+        this(factory,DEFAULT_MAX_ACTIVE,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     * @param config a non-<tt>null</tt> {@link GenericKeyedObjectPool.Config} describing my configuration
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, GenericKeyedObjectPool.Config config) {
+        this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive) {
+        this(factory,maxActive,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
+     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (per key) (see {@link #setMaxIdle})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)KeyedPoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
+     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericKeyedObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (per key) (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
+     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
+     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        _factory = factory;
+        _maxActive = maxActive;
+        switch(whenExhaustedAction) {
+            case WHEN_EXHAUSTED_BLOCK:
+            case WHEN_EXHAUSTED_FAIL:
+            case WHEN_EXHAUSTED_GROW:
+                _whenExhaustedAction = whenExhaustedAction;
+                break;
+            default:
+                throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized.");
+        }
+        _maxWait = maxWait;
+        _maxIdle = maxIdle;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _testWhileIdle = testWhileIdle;
+
+        _poolMap = new HashMap();
+        _activeMap = new HashMap();
+        _poolList = new CursorableLinkedList();
+
+        if(_timeBetweenEvictionRunsMillis > 0) {
+            _evictor = new Evictor();
+            Thread t = new Thread(_evictor);
+            t.setDaemon(true);
+            t.start();
+        }
+    }
+
+    //--- public methods ---------------------------------------------
+
+    //--- configuration methods --------------------------------------
+
+    /**
+     * Returns the cap on the total number of active instances from my pool.
+     * @return the cap on the total number of active instances from my pool.
+     * @see #setMaxActive
+     */
+    public int getMaxActive() {
+        return _maxActive;
+    }
+
+    /**
+     * Sets the cap on the total number of active instances from my pool.
+     * @param maxActive The cap on the total number of active instances from my pool.
+     *                  Use a negative value for an infinite number of instances.
+     * @see #getMaxActive
+     */
+    public void setMaxActive(int maxActive) {
+        _maxActive = maxActive;
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * Returns the action to take when the {@link #borrowObject} method
+     * is invoked when the pool is exhausted (the maximum number
+     * of "active" objects has been reached).
+     *
+     * @return one of {@link #WHEN_EXHAUSTED_BLOCK}, {@link #WHEN_EXHAUSTED_FAIL} or {@link #WHEN_EXHAUSTED_GROW}
+     * @see #setWhenExhaustedAction
+     */
+    public byte getWhenExhaustedAction() {
+        return _whenExhaustedAction;
+    }
+
+    /**
+     * Sets the action to take when the {@link #borrowObject} method
+     * is invoked when the pool is exhausted (the maximum number
+     * of "active" objects has been reached).
+     *
+     * @param whenExhaustedAction the action code, which must be one of
+     *        {@link #WHEN_EXHAUSTED_BLOCK}, {@link #WHEN_EXHAUSTED_FAIL},
+     *        or {@link #WHEN_EXHAUSTED_GROW}
+     * @see #getWhenExhaustedAction
+     */
+    public synchronized void setWhenExhaustedAction(byte whenExhaustedAction) {
+        switch(whenExhaustedAction) {
+            case WHEN_EXHAUSTED_BLOCK:
+            case WHEN_EXHAUSTED_FAIL:
+            case WHEN_EXHAUSTED_GROW:
+                _whenExhaustedAction = whenExhaustedAction;
+                notifyAll();
+                break;
+            default:
+                throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized.");
+        }
+    }
+
+
+    /**
+     * Returns the maximum amount of time (in milliseconds) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #setWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     *
+     * When less than 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #setMaxWait
+     * @see #setWhenExhaustedAction
+     * @see #WHEN_EXHAUSTED_BLOCK
+     */
+    public synchronized long getMaxWait() {
+        return _maxWait;
+    }
+
+    /**
+     * Sets the maximum amount of time (in milliseconds) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #setWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     *
+     * When less than 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #getMaxWait
+     * @see #setWhenExhaustedAction
+     * @see #WHEN_EXHAUSTED_BLOCK
+     */
+    public synchronized void setMaxWait(long maxWait) {
+        _maxWait = maxWait;
+    }
+
+    /**
+     * Returns the cap on the number of "idle" instances in the pool.
+     * @return the cap on the number of "idle" instances in the pool.
+     * @see #setMaxIdle
+     */
+    public int getMaxIdle() {
+        return _maxIdle;
+    }
+
+    /**
+     * Sets the cap on the number of "idle" instances in the pool.
+     * @param maxIdle The cap on the number of "idle" instances in the pool.
+     *                Use a negative value to indicate an unlimited number
+     *                of idle instances.
+     * @see #getMaxIdle
+     */
+    public void setMaxIdle(int maxIdle) {
+        _maxIdle = maxIdle;
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned by the {@link #borrowObject}
+     * method.  If the object fails to validate,
+     * it will be dropped from the pool, and we will attempt
+     * to borrow another.
+     *
+     * @see #setTestOnBorrow
+     */
+    public boolean getTestOnBorrow() {
+        return _testOnBorrow;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned by the {@link #borrowObject}
+     * method.  If the object fails to validate,
+     * it will be dropped from the pool, and we will attempt
+     * to borrow another.
+     *
+     * @see #getTestOnBorrow
+     */
+    public void setTestOnBorrow(boolean testOnBorrow) {
+        _testOnBorrow = testOnBorrow;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #setTestOnReturn
+     */
+    public boolean getTestOnReturn() {
+        return _testOnReturn;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #getTestOnReturn
+     */
+    public void setTestOnReturn(boolean testOnReturn) {
+        _testOnReturn = testOnReturn;
+    }
+
+    /**
+     * Returns the number of milliseconds to sleep between runs of the
+     * idle object evictor thread.
+     * When non-positive, no idle object evictor thread will be
+     * run.
+     *
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized long getTimeBetweenEvictionRunsMillis() {
+        return _timeBetweenEvictionRunsMillis;
+    }
+
+    /**
+     * Sets the number of milliseconds to sleep between runs of the
+     * idle object evictor thread.
+     * When non-positive, no idle object evictor thread will be
+     * run.
+     *
+     * @see #getTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
+        if(_timeBetweenEvictionRunsMillis > 0 && timeBetweenEvictionRunsMillis <= 0) {
+            _evictor.cancel();
+            _evictor = null;
+            _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        } else if(_timeBetweenEvictionRunsMillis <= 0 && timeBetweenEvictionRunsMillis > 0) {
+            _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+            _evictor = new Evictor();
+            Thread t = new Thread(_evictor);
+            t.setDaemon(true);
+            t.start();
+        } else {
+            _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        }
+    }
+
+    /**
+     * Returns the number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     *
+     * @see #setNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public int getNumTestsPerEvictionRun() {
+        return _numTestsPerEvictionRun;
+    }
+
+    /**
+     * Sets the number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     * <p>
+     * When a negative value is supplied, <tt>ceil({@link #numIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
+     * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
+     * idle objects will be tested per run.
+     *
+     * @see #getNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+    }
+
+    /**
+     * Returns the minimum amount of time an object may sit idle in the pool
+     * before it is eligable for eviction by the idle object evictor
+     * (if any).
+     *
+     * @see #setMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized long getMinEvictableIdleTimeMillis() {
+        return _minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * Sets the minimum amount of time an object may sit idle in the pool
+     * before it is eligable for eviction by the idle object evictor
+     * (if any).
+     * When non-positive, no objects will be evicted from the pool
+     * due to idle time alone.
+     *
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @see #setTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public boolean getTestWhileIdle() {
+        return _testWhileIdle;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @see #getTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public void setTestWhileIdle(boolean testWhileIdle) {
+        _testWhileIdle = testWhileIdle;
+    }
+
+    /**
+     * Sets my configuration.
+     * @see GenericKeyedObjectPool.Config
+     */
+    public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {
+        setMaxIdle(conf.maxIdle);
+        setMaxActive(conf.maxActive);
+        setMaxWait(conf.maxWait);
+        setWhenExhaustedAction(conf.whenExhaustedAction);
+        setTestOnBorrow(conf.testOnBorrow);
+        setTestOnReturn(conf.testOnReturn);
+        setTestWhileIdle(conf.testWhileIdle);
+        setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
+        setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
+        setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
+    }
+
+    //-- ObjectPool methods ------------------------------------------
+
+    public synchronized Object borrowObject(Object key) {
+        long starttime = System.currentTimeMillis();
+        for(;;) {
+            CursorableLinkedList pool = (CursorableLinkedList)(_poolMap.get(key));
+            if(null == pool) {
+                pool = new CursorableLinkedList();
+                _poolMap.put(key,pool);
+                _poolList.add(key);
+            }
+            ObjectTimestampPair pair = null;
+            // if there are any sleeping, just grab one of those
+            try {
+                pair = (ObjectTimestampPair)(pool.removeFirst());
+                if(null != pair) {
+                    _totalIdle--;
+                }
+            } catch(NoSuchElementException e) { /* ignored */
+            }
+            // otherwise
+            if(null == pair) {
+                // check if we can create one
+                // (note we know that the num sleeping is 0, else we wouldn't be here)
+                int active = 0;
+                Integer act = (Integer)(_activeMap.get(key));
+                if(null != act) {
+                    active = act.intValue();
+                }
+                if(_maxActive > 0 && active < _maxActive) {
+                    Object obj = _factory.makeObject(key);
+                    pair = new ObjectTimestampPair(obj);
+                } else {
+                    // the pool is exhausted
+                    switch(_whenExhaustedAction) {
+                        case WHEN_EXHAUSTED_GROW:
+                            Object obj = _factory.makeObject(key);
+                            pair = new ObjectTimestampPair(obj);
+                            break;
+                        case WHEN_EXHAUSTED_FAIL:
+                            throw new NoSuchElementException();
+                        case WHEN_EXHAUSTED_BLOCK:
+                            try {
+                                if(_maxWait <= 0) {
+                                    wait();
+                                } else {
+                                    wait(_maxWait);
+                                }
+                            } catch(InterruptedException e) {
+                                // ignored
+                            }
+                            if(_maxWait > 0 && ((System.currentTimeMillis() - starttime) >= _maxWait)) {
+                                throw new NoSuchElementException("Timeout waiting for idle object");
+                            } else {
+                                continue; // keep looping
+                            }
+                        default:
+                            throw new IllegalArgumentException("whenExhaustedAction " + _whenExhaustedAction + " not recognized.");
+                    }
+                }
+            }
+            _factory.activateObject(key,pair.value);
+            if(_testOnBorrow && !_factory.validateObject(key,pair.value)) {
+                try {
+                    _factory.passivateObject(key,pair.value);
+                } catch(Exception e) {
+                    ; // ignored, we're throwing it out anyway
+                }
+                _factory.destroyObject(key,pair.value);
+            } else {
+                Integer active = (Integer)(_activeMap.get(key));
+                if(null == active) {
+                    _activeMap.put(key,new Integer(1));
+                } else {
+                    _activeMap.put(key,new Integer(active.intValue() + 1));
+                }
+                _totalActive++;
+                return pair.value;
+            }
+        }
+    }
+
+    public synchronized void clear() throws UnsupportedOperationException {
+        Iterator keyiter = _poolList.iterator();
+        while(keyiter.hasNext()) {
+            Object key = keyiter.next();
+            CursorableLinkedList list = (CursorableLinkedList)(_poolMap.get(key));
+            Iterator it = list.iterator();
+            while(it.hasNext()) {
+                _factory.destroyObject(key,((ObjectTimestampPair)(it.next())).value);
+                it.remove();
+            }
+        }
+        _poolMap.clear();
+        _poolList.clear();
+        _totalIdle = 0;
+        notifyAll();
+    }
+
+    public synchronized void clear(Object key) throws UnsupportedOperationException {
+        CursorableLinkedList pool = (CursorableLinkedList)(_poolMap.remove(key));
+        if(null == pool) {
+            return;
+        } else {
+            _poolList.remove(key);
+            Iterator it = pool.iterator();
+            while(it.hasNext()) {
+                _factory.destroyObject(key,((ObjectTimestampPair)(it.next())).value);
+                it.remove();
+                _totalIdle--;
+            }
+        }
+        notifyAll();
+    }
+
+    public int numActive() {
+        return _totalActive;
+    }
+
+    public int numIdle() {
+        return _totalIdle;
+    }
+
+    public synchronized int numActive(Object key) {
+        try {
+            return((Integer)(_activeMap.get(key))).intValue();
+        } catch(Exception e) {
+            return 0;
+        }
+    }
+
+    public synchronized int numIdle(Object key) {
+        try {
+            return((CursorableLinkedList)(_poolMap.get(key))).size();
+        } catch(Exception e) {
+            return 0;
+        }
+    }
+
+    public synchronized void returnObject(Object key, Object obj) {
+        _totalActive--;
+
+        CursorableLinkedList pool = (CursorableLinkedList)(_poolMap.get(key));
+        if(null == pool) {
+            pool = new CursorableLinkedList();
+            _poolMap.put(key,pool);
+            _poolList.add(key);
+        }
+
+        Integer active = (Integer)(_activeMap.get(key));
+        if(null == active) {
+            // do nothing, either null or zero is OK
+        } else if(active.intValue() <= 1) {
+            _activeMap.remove(key);
+        } else {
+            _activeMap.put(key,new Integer(active.intValue() - 1));
+        }
+
+        if(_maxIdle > 0 && (pool.size() >= _maxIdle || (_testOnReturn && !_factory.validateObject(key,obj)))) {
+            try {
+                _factory.passivateObject(key,obj);
+            } catch(Exception e) {
+                ; // ignored, we're throwing it out anyway
+            }
+            _factory.destroyObject(key,obj);
+        } else {
+            try {
+                _factory.passivateObject(key,obj);
+                pool.addFirst(new ObjectTimestampPair(obj));
+                _totalIdle++;
+            } catch(Exception e) {
+                _factory.destroyObject(key,obj);
+            }
+        }
+        notifyAll();
+    }
+
+    synchronized public void close() {
+        clear();
+        _poolList = null;
+        _poolMap = null;
+        _activeMap = null;
+        if(null != _evictor) {
+            _evictor.cancel();
+            _evictor = null;
+        }
+    }
+
+    synchronized public void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
+        if(0 < numActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+
+    //--- package methods --------------------------------------------
+
+    synchronized String debugInfo() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Active: ").append(numActive()).append("\n");
+        buf.append("Idle: ").append(numIdle()).append("\n");
+        Iterator it = _poolList.iterator();
+        while(it.hasNext()) {
+            buf.append("\t").append(_poolMap.get(it.next())).append("\n");
+        }
+        return buf.toString();
+    }
+
+
+    //--- inner classes ----------------------------------------------
+
+    /**
+     * A simple "struct" encapsulating an object instance and a timestamp.
+     */
+    class ObjectTimestampPair {
+        Object value;
+        long tstamp;
+
+        ObjectTimestampPair(Object val) {
+            value = val;
+            tstamp = System.currentTimeMillis();
+        }
+
+        ObjectTimestampPair(Object val, long time) {
+            value = val;
+            tstamp = time;
+        }
+
+        public String toString() {
+            return value + ";" + tstamp;
+        }
+    }
+
+    /**
+     * The idle object evictor thread.
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    class Evictor implements Runnable {
+        protected boolean _cancelled = false;
+
+        void cancel() {
+            _cancelled = true;
+        }
+
+        public void run() {
+            CursorableLinkedList.Cursor objcursor = null;
+            CursorableLinkedList.Cursor keycursor = null;
+            Object key = null;
+
+            while(!_cancelled) {
+                long sleeptime = 0L;
+                synchronized(GenericKeyedObjectPool.this) {
+                    sleeptime = _timeBetweenEvictionRunsMillis;
+                }
+                try {
+                    Thread.currentThread().sleep(sleeptime);
+                } catch(Exception e) {
+                    ; // ignored
+                }
+                try {
+                    synchronized(GenericKeyedObjectPool.this) {
+                        for(int i=0,m=getNumTests();i<m;i++) {
+                            if(_poolMap.size() > 0) {
+                                // if we don't have a key cursor, then create one, and close any object cursor
+                                if(null == keycursor) {
+                                    keycursor = _poolList.cursor();
+                                    key = null;
+                                    if(null != objcursor) {
+                                        objcursor.close();
+                                        objcursor = null;
+                                    }
+                                }
+                                // if we don't have an object cursor
+                                if(null == objcursor) {
+                                    // if the keycursor has a next value, then use it
+                                    if(keycursor.hasNext()) {
+                                        key = keycursor.next();
+                                        CursorableLinkedList pool = (CursorableLinkedList)(_poolMap.get(key));
+                                        objcursor = pool.cursor(pool.size());
+                                    } else {
+                                        // else close the key cursor and loop back around
+                                        if(null != keycursor) {
+                                            keycursor.close();
+                                            keycursor = _poolList.cursor();
+                                            if(null != objcursor) {
+                                                objcursor.close();
+                                                objcursor = null;
+                                            }
+                                        }
+                                        continue;
+                                    }
+                                }
+                                // if the objcursor has a previous object, then test it
+                                if(objcursor.hasPrevious()) {
+                                    ObjectTimestampPair pair = (ObjectTimestampPair)(objcursor.previous());
+                                    if(System.currentTimeMillis() - pair.tstamp > _minEvictableIdleTimeMillis) {
+                                        try {
+                                            objcursor.remove();
+                                            _totalIdle--;
+                                            _factory.destroyObject(key,pair.value);
+
+                                            // if that was the last object for that key, drop that pool
+                                            if( ((CursorableLinkedList)(_poolMap.get(key))).isEmpty() ) {
+                                                _poolMap.remove(key);
+                                                _poolList.remove(key);
+                                            }
+
+
+                                        } catch(Exception e) {
+                                            ; // ignored
+                                        }
+                                    } else if(_testWhileIdle) {
+                                        boolean active = false;
+                                        try {
+                                            _factory.activateObject(key,pair.value);
+                                            active = true;
+                                        } catch(Exception e) {
+                                            objcursor.remove();
+                                            try {
+                                                _factory.passivateObject(key,pair.value);
+                                            } catch(Exception ex) {
+                                                ; // ignored
+                                            }
+                                            _factory.destroyObject(key,pair.value);
+                                        }
+                                        if(active) {
+                                            if(!_factory.validateObject(key,pair.value)) {
+                                                try {
+                                                    objcursor.remove();
+                                                    _totalIdle--;
+                                                    try {
+                                                        _factory.passivateObject(key,pair.value);
+                                                    } catch(Exception e) {
+                                                        ; // ignored
+                                                    }
+                                                    _factory.destroyObject(key,pair.value);
+                                                    if( ((CursorableLinkedList)(_poolMap.get(key))).isEmpty() ) {
+                                                        _poolMap.remove(key);
+                                                        _poolList.remove(key);
+                                                    }
+
+                                                } catch(Exception e) {
+                                                    ; // ignored
+                                                }
+                                            } else {
+                                                try {
+                                                    _factory.passivateObject(key,pair.value);
+                                                } catch(Exception e) {
+                                                    objcursor.remove();
+                                                    _totalIdle--;
+                                                    _factory.destroyObject(key,pair.value);
+                                                    if( ((CursorableLinkedList)(_poolMap.get(key))).isEmpty() ) {
+                                                        _poolMap.remove(key);
+                                                        _poolList.remove(key);
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                } else {
+                                    // else the objcursor is done, so close it and loop around
+                                    if(objcursor != null) {
+                                        objcursor.close();
+                                        objcursor = null;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } catch(Exception e) {
+                    ; // ignored
+                }
+            }
+            if(null != keycursor) {
+                keycursor.close();
+            }
+            if(null != objcursor) {
+                objcursor.close();
+            }
+        }
+
+        private int getNumTests() {
+            if(_numTestsPerEvictionRun >= 0) {
+                return _numTestsPerEvictionRun;
+            } else {
+                return(int)(Math.ceil((double)_totalIdle/Math.abs((double)_numTestsPerEvictionRun)));
+            }
+        }
+    }
+
+    /**
+     * A simple "struct" encapsulating the
+     * configuration information for a {@link GenericKeyedObjectPool}.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory,GenericKeyedObjectPool.Config)
+     * @see GenericKeyedObjectPool#setConfig
+     */
+    public static class Config {
+        public int maxIdle = GenericKeyedObjectPool.DEFAULT_MAX_IDLE;
+        public int maxActive = GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE;
+        public long maxWait = GenericKeyedObjectPool.DEFAULT_MAX_WAIT;
+        public byte whenExhaustedAction = GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+        public boolean testOnBorrow = GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW;
+        public boolean testOnReturn = GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN;
+        public boolean testWhileIdle = GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE;
+        public long timeBetweenEvictionRunsMillis = GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+        public int numTestsPerEvictionRun =  GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+        public long minEvictableIdleTimeMillis = GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    }
+
+    //--- protected attributes ---------------------------------------
+
+    /**
+     * The cap on the number of idle instances in the pool (per key).
+     * @see #setMaxIdle
+     * @see #getMaxIdle
+     */
+    protected int _maxIdle = DEFAULT_MAX_IDLE;
+
+    /**
+     * The cap on the total number of active instances from the pool (per key).
+     * @see #setMaxActive
+     * @see #getMaxActive
+     */
+    protected int _maxActive = DEFAULT_MAX_ACTIVE;
+
+    /**
+     * The maximum amount of time (in millis) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #getWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     *
+     * When less than 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #setMaxWait
+     * @see #getMaxWait
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #setWhenExhaustedAction
+     * @see #getWhenExhaustedAction
+     */
+    protected long _maxWait = DEFAULT_MAX_WAIT;
+
+    /**
+     * The action to take when the {@link #borrowObject} method
+     * is invoked when the pool is exhausted (the maximum number
+     * of "active" objects has been reached).
+     *
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #DEFAULT_WHEN_EXHAUSTED_ACTION
+     * @see #setWhenExhaustedAction
+     * @see #getWhenExhaustedAction
+     */
+    protected byte _whenExhaustedAction = DEFAULT_WHEN_EXHAUSTED_ACTION;
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned by the {@link #borrowObject}
+     * method.  If the object fails to validate,
+     * it will be dropped from the pool, and we will attempt
+     * to borrow another.
+     *
+     * @see #setTestOnBorrow
+     * @see #getTestOnBorrow
+     */
+    protected boolean _testOnBorrow = DEFAULT_TEST_ON_BORROW;
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #getTestOnReturn
+     * @see #setTestOnReturn
+     */
+    protected boolean _testOnReturn = DEFAULT_TEST_ON_RETURN;
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @see #setTestWhileIdle
+     * @see #getTestWhileIdle
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    protected boolean _testWhileIdle = DEFAULT_TEST_WHILE_IDLE;
+
+    /**
+     * The number of milliseconds to sleep between runs of the
+     * idle object evictor thread.
+     * When non-positive, no idle object evictor thread will be
+     * run.
+     *
+     * @see #setTimeBetweenEvictionRunsMillis
+     * @see #getTimeBetweenEvictionRunsMillis
+     */
+    protected long _timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+
+    /**
+     * The number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     * <p>
+     * When a negative value is supplied, <tt>ceil({@link #numIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
+     * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
+     * idle objects will be tested per run.
+     *
+     * @see #setNumTestsPerEvictionRun
+     * @see #getNumTestsPerEvictionRun
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    protected int _numTestsPerEvictionRun =  DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+
+    /**
+     * The minimum amount of time an object may sit idle in the pool
+     * before it is eligable for eviction by the idle object evictor
+     * (if any).
+     * When non-positive, no objects will be evicted from the pool
+     * due to idle time alone.
+     *
+     * @see #setMinEvictableIdleTimeMillis
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    protected long _minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+
+    /** My hash of pools (CursorableLinkedLists). */
+    protected HashMap _poolMap = null;
+
+    /**
+     * A cursorable list of my pools.
+     * @see GenericKeyedObjectPool.Evictor#run
+     */
+    protected CursorableLinkedList _poolList = null;
+
+    /** Count of active objects, per key. */
+    protected HashMap _activeMap = null;
+
+    /** The total number of active instances. */
+    protected int _totalActive = 0;
+
+    /** The total number of idle instances. */
+    protected int _totalIdle = 0;
+
+
+    /** My {@link KeyedPoolableObjectFactory}. */
+    protected KeyedPoolableObjectFactory _factory = null;
+
+    /**
+     * My idle object eviction thread, if any.
+     */
+    protected Evictor _evictor = null;
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPoolFactory.java b/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPoolFactory.java
new file mode 100644
index 0000000..8742bca
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPoolFactory.java
@@ -0,0 +1,136 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPoolFactory.java,v 1.1 2001/04/14 16:41:24 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:24 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+
+/**
+ * A factory for creating {@link GenericKeyedObjectPool} instances.
+ *
+ * @see GenericKeyedObjectPool
+ * @see KeyedObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: GenericKeyedObjectPoolFactory.java,v 1.1 2001/04/14 16:41:24 rwaldhoff Exp $
+ */
+public class GenericKeyedObjectPoolFactory implements KeyedObjectPoolFactory {
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory) {
+        this(factory,GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE,GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericKeyedObjectPool.DEFAULT_MAX_WAIT,GenericKeyedObjectPool.DEFAULT_MAX_IDLE,GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, GenericKeyedObjectPool.Config config) {
+        this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int maxActive) {
+        this(factory,maxActive,GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericKeyedObjectPool.DEFAULT_MAX_WAIT,GenericKeyedObjectPool.DEFAULT_MAX_IDLE,GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericKeyedObjectPool.DEFAULT_MAX_IDLE,GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericKeyedObjectPool.DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        _maxIdle = maxIdle;
+        _maxActive = maxActive;
+        _maxWait = maxWait;
+        _whenExhaustedAction = whenExhaustedAction;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _testWhileIdle = testWhileIdle;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _factory = factory;
+    }
+
+    public KeyedObjectPool createPool() {
+        return new GenericKeyedObjectPool(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle);
+    }
+
+    //--- protected attributes ---------------------------------------
+
+    protected int _maxIdle = GenericKeyedObjectPool.DEFAULT_MAX_IDLE;
+    protected int _maxActive = GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE;
+    protected long _maxWait = GenericKeyedObjectPool.DEFAULT_MAX_WAIT;
+    protected byte _whenExhaustedAction = GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+    protected boolean _testOnBorrow = GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW;
+    protected boolean _testOnReturn = GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN;
+    protected boolean _testWhileIdle = GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE;
+    protected long _timeBetweenEvictionRunsMillis = GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+    protected int _numTestsPerEvictionRun =  GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+    protected long _minEvictableIdleTimeMillis = GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    protected KeyedPoolableObjectFactory _factory = null;
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/GenericObjectPool.java b/src/java/org/apache/commons/pool/impl/GenericObjectPool.java
new file mode 100644
index 0000000..fa671bb
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericObjectPool.java
@@ -0,0 +1,1126 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/GenericObjectPool.java,v 1.1 2001/04/14 16:41:43 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:43 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+import org.apache.commons.collections.CursorableLinkedList;
+import org.apache.commons.collections.CursorableLinkedList.Cursor;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+import java.util.EmptyStackException;
+import java.util.ListIterator;
+
+/**
+ * A configurable {@link ObjectPool} implementation.
+ * <p>
+ * When coupled with the appropriate {@link PoolableObjectFactory},
+ * <tt>GenericObjectPool</tt> provides robust pooling functionality for
+ * arbitrary objects.
+ * <p>
+ * A <tt>GenericObjectPool</tt> provides a number of configurable parameters:
+ * <ul>
+ *  <li>
+ *    {@link #setMaxActive <i>maxActive</i>} controls the maximum number of objects that can
+ *    be borrowed from the pool at one time.  When non-positive, there
+ *    is no limit to the number of objects that may be active at one time.
+ *    When {@link #setMaxActive <i>maxActive</i>} is exceeded, the pool is said to be exhausted.
+ *  </li>
+ *  <li>
+ *    {@link #setMaxIdle <i>maxIdle</i>} controls the maximum number of objects that can
+ *    sit idle in the pool at any time.  When non-positive, there
+ *    is no limit to the number of objects that may be idle at one time.
+ *  </li>
+ *  <li>
+ *    {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} specifies the
+ *    behaviour of the {@link #borrowObject} method when the pool is exhausted:
+ *    <ul>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} is
+ *      {@link #WHEN_EXHAUSTED_FAIL}, {@link #borrowObject} will throw
+ *      a {@link NoSuchElementException}
+ *    </li>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} is
+ *      {@link #WHEN_EXHAUSTED_GROW}, {@link #borrowObject} will create a new
+ *      object and return it(essentially making {@link #setMaxActive <i>maxActive</i>}
+ *      meaningless.)
+ *    </li>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>}
+ *      is {@link #WHEN_EXHAUSTED_BLOCK}, {@link #borrowObject} will block
+ *      (invoke {@link Object#wait} until a new or idle object is available.
+ *      If a positive {@link #setMaxWait <i>maxWait</i>}
+ *      value is supplied, the {@link #borrowObject} will block for at
+ *      most that many milliseconds, after which a {@link NoSuchElementException}
+ *      will be thrown.  If {@link #setMaxWait <i>maxWait</i>} is non-positive,
+ *      the {@link #borrowObject} method will block indefinitely.
+ *    </li>
+ *    </ul>
+ *  </li>
+ *  <li>
+ *    When {@link #setTestOnBorrow <i>testOnBorrow</i>} is set, the pool will
+ *    attempt to validate each object before it is returned from the
+ *    {@link #borrowObject} method. (Using the provided factory's
+ *    {@link PoolableObjectFactory#validateObject} method.)  Objects that fail
+ *    to validate will be dropped from the pool, and a different object will
+ *    be borrowed.
+ *  </li>
+ *  <li>
+ *    When {@link #setTestOnReturn <i>testOnReturn</i>} is set, the pool will
+ *    attempt to validate each object before it is returned to the pool in the
+ *    {@link #returnObject} method. (Using the provided factory's
+ *    {@link PoolableObjectFactory#validateObject}
+ *    method.)  Objects that fail to validate will be dropped from the pool.
+ *  </li>
+ * </ul>
+ * <p>
+ * Optionally, one may configure the pool to examine and possibly evict objects as they
+ * sit idle in the pool.  This is performed by an "idle object eviction" thread, which
+ * runs asychronously.  The idle object eviction thread may be configured using the
+ * following attributes:
+ * <ul>
+ *  <li>
+ *   {@link #setTimeBetweenEvictionRunsMillis <i>timeBetweenEvictionRunsMillis</i>}
+ *   indicates how long the eviction thread should sleep before "runs" of examining
+ *   idle objects.  When non-positive, no eviction thread will be launched.
+ *  </li>
+ *  <li>
+ *   {@link #setMinEvictableIdleTimeMillis <i>minEvictableIdleTimeMillis</i>}
+ *   specifies the minimum amount of time that an object may sit idle in the pool
+ *   before it is eligable for eviction due to idle time.  When non-positive, no object
+ *   will be dropped from the pool due to idle time alone.
+ *  </li>
+ *  <li>
+ *   {@link #setTestWhileIdle <i>testWhileIdle</i>} indicates whether or not idle
+ *   objects should be validated using the factory's
+ *   {@link PoolableObjectFactory#validateObject} method.  Objects
+ *   that fail to validate will be dropped from the pool.
+ *  </li>
+ * </ul>
+ * @see GenericKeyedObjectPool
+ * @author Rodney Waldhoff
+ * @version $Id: GenericObjectPool.java,v 1.1 2001/04/14 16:41:43 rwaldhoff Exp $
+ */
+public class GenericObjectPool implements ObjectPool {
+
+    //--- public constants -------------------------------------------
+
+    /**
+     * A "when exhausted action" type indicating that when the pool is
+     * exhausted (i.e., the maximum number of active objects has
+     * been reached), the {@link #borrowObject}
+     * method should fail, throwing a {@link NoSuchElementException}.
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte WHEN_EXHAUSTED_FAIL   = 0;
+
+    /**
+     * A "when exhausted action" type indicating that when the pool
+     * is exhausted (i.e., the maximum number
+     * of active objects has been reached), the {@link #borrowObject}
+     * method should block until a new object is available, or the
+     * {@link #getMaxWait maximum wait time} has been reached.
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setMaxWait
+     * @see #getMaxWait
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte WHEN_EXHAUSTED_BLOCK  = 1;
+
+    /**
+     * A "when exhausted action" type indicating that when the pool is
+     * exhausted (i.e., the maximum number
+     * of active objects has been reached), the {@link #borrowObject}
+     * method should simply create a new object anyway.
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte WHEN_EXHAUSTED_GROW   = 2;
+
+    /**
+     * The default cap on the number of "sleeping" instances in the pool.
+     * @see #getMaxIdle
+     * @see #setMaxIdle
+     */
+    public static final int DEFAULT_MAX_IDLE  = 8;
+
+    /**
+     * The default cap on the total number of active instances from the pool.
+     * @see #getMaxActive
+     */
+    public static final int DEFAULT_MAX_ACTIVE  = 8;
+
+    /**
+     * The default "when exhausted action" for the pool.
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #setWhenExhaustedAction
+     */
+    public static final byte DEFAULT_WHEN_EXHAUSTED_ACTION = WHEN_EXHAUSTED_BLOCK;
+
+    /**
+     * The default maximum amount of time (in millis) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #getWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     * @see #getMaxWait
+     * @see #setMaxWait
+     */
+    public static final long DEFAULT_MAX_WAIT = -1L;
+
+    /**
+     * The default "test on borrow" value.
+     * @see #getTestOnBorrow
+     * @see #setTestOnBorrow
+     */
+    public static final boolean DEFAULT_TEST_ON_BORROW = false;
+
+    /**
+     * The default "test on return" value.
+     * @see #getTestOnReturn
+     * @see #setTestOnReturn
+     */
+    public static final boolean DEFAULT_TEST_ON_RETURN = false;
+
+    /**
+     * The default "test while idle" value.
+     * @see #getTestWhileIdle
+     * @see #setTestWhileIdle
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public static final boolean DEFAULT_TEST_WHILE_IDLE = false;
+
+    /**
+     * The default "time between eviction runs" value.
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public static final long DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L;
+
+    /**
+     * The default number of objects to examine per run in the
+     * idle object evictor.
+     * @see #getNumTestsPerEvictionRun
+     * @see #setNumTestsPerEvictionRun
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3;
+
+    /**
+     * The default value for {@link #getMinEvictableIdleTimeMillis}.
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #setMinEvictableIdleTimeMillis
+     */
+    public static final long DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L;
+
+    //--- constructors -----------------------------------------------
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     */
+    public GenericObjectPool(PoolableObjectFactory factory) {
+        this(factory,DEFAULT_MAX_ACTIVE,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param config a non-<tt>null</tt> {@link GenericObjectPool.Config} describing my configuration
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) {
+        this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive) {
+        this(factory,maxActive,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
+     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #getTestOnReturn})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link #getMaxIdle})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link #getMaxIdle})
+     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #getTestOnReturn})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> using the specified values.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
+     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
+     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
+     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
+     */
+    public GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        _factory = factory;
+        _maxActive = maxActive;
+        switch(whenExhaustedAction) {
+            case WHEN_EXHAUSTED_BLOCK:
+            case WHEN_EXHAUSTED_FAIL:
+            case WHEN_EXHAUSTED_GROW:
+                _whenExhaustedAction = whenExhaustedAction;
+                break;
+            default:
+                throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized.");
+        }
+        _maxWait = maxWait;
+        _maxIdle = maxIdle;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _testWhileIdle = testWhileIdle;
+
+        _pool = new CursorableLinkedList();
+        if(_timeBetweenEvictionRunsMillis > 0) {
+            _evictor = new Evictor();
+            Thread t = new Thread(_evictor);
+            t.setDaemon(true);
+            t.start();
+        }
+    }
+
+    //--- public methods ---------------------------------------------
+
+    //--- configuration methods --------------------------------------
+
+    /**
+     * Returns the cap on the total number of active instances from my pool.
+     * @return the cap on the total number of active instances from my pool.
+     * @see #setMaxActive
+     */
+    public int getMaxActive() {
+        return _maxActive;
+    }
+
+    /**
+     * Sets the cap on the total number of active instances from my pool.
+     * @param maxActive The cap on the total number of active instances from my pool.
+     *                  Use a negative value for an infinite number of instances.
+     * @see #getMaxActive
+     */
+    public void setMaxActive(int maxActive) {
+        _maxActive = maxActive;
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * Returns the action to take when the {@link #borrowObject} method
+     * is invoked when the pool is exhausted (the maximum number
+     * of "active" objects has been reached).
+     *
+     * @return one of {@link #WHEN_EXHAUSTED_BLOCK}, {@link #WHEN_EXHAUSTED_FAIL} or {@link #WHEN_EXHAUSTED_GROW}
+     * @see #setWhenExhaustedAction
+     */
+    public byte getWhenExhaustedAction() {
+        return _whenExhaustedAction;
+    }
+
+    /**
+     * Sets the action to take when the {@link #borrowObject} method
+     * is invoked when the pool is exhausted (the maximum number
+     * of "active" objects has been reached).
+     *
+     * @param whenExhaustedAction the action code, which must be one of
+     *        {@link #WHEN_EXHAUSTED_BLOCK}, {@link #WHEN_EXHAUSTED_FAIL},
+     *        or {@link #WHEN_EXHAUSTED_GROW}
+     * @see #getWhenExhaustedAction
+     */
+    public synchronized void setWhenExhaustedAction(byte whenExhaustedAction) {
+        switch(whenExhaustedAction) {
+            case WHEN_EXHAUSTED_BLOCK:
+            case WHEN_EXHAUSTED_FAIL:
+            case WHEN_EXHAUSTED_GROW:
+                _whenExhaustedAction = whenExhaustedAction;
+                notifyAll();
+                break;
+            default:
+                throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction + " not recognized.");
+        }
+    }
+
+
+    /**
+     * Returns the maximum amount of time (in milliseconds) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #setWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     *
+     * When less than 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #setMaxWait
+     * @see #setWhenExhaustedAction
+     * @see #WHEN_EXHAUSTED_BLOCK
+     */
+    public synchronized long getMaxWait() {
+        return _maxWait;
+    }
+
+    /**
+     * Sets the maximum amount of time (in milliseconds) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #setWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     *
+     * When less than 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #getMaxWait
+     * @see #setWhenExhaustedAction
+     * @see #WHEN_EXHAUSTED_BLOCK
+     */
+    public synchronized void setMaxWait(long maxWait) {
+        _maxWait = maxWait;
+    }
+
+    /**
+     * Returns the cap on the number of "idle" instances in the pool.
+     * @return the cap on the number of "idle" instances in the pool.
+     * @see #setMaxIdle
+     */
+    public int getMaxIdle() {
+        return _maxIdle;
+    }
+
+    /**
+     * Sets the cap on the number of "idle" instances in the pool.
+     * @param maxIdle The cap on the number of "idle" instances in the pool.
+     *                Use a negative value to indicate an unlimited number
+     *                of idle instances.
+     * @see #getMaxIdle
+     */
+    public void setMaxIdle(int maxIdle) {
+        _maxIdle = maxIdle;
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned by the {@link #borrowObject}
+     * method.  If the object fails to validate,
+     * it will be dropped from the pool, and we will attempt
+     * to borrow another.
+     *
+     * @see #setTestOnBorrow
+     */
+    public boolean getTestOnBorrow() {
+        return _testOnBorrow;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned by the {@link #borrowObject}
+     * method.  If the object fails to validate,
+     * it will be dropped from the pool, and we will attempt
+     * to borrow another.
+     *
+     * @see #getTestOnBorrow
+     */
+    public void setTestOnBorrow(boolean testOnBorrow) {
+        _testOnBorrow = testOnBorrow;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #setTestOnReturn
+     */
+    public boolean getTestOnReturn() {
+        return _testOnReturn;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #getTestOnReturn
+     */
+    public void setTestOnReturn(boolean testOnReturn) {
+        _testOnReturn = testOnReturn;
+    }
+
+    /**
+     * Returns the number of milliseconds to sleep between runs of the
+     * idle object evictor thread.
+     * When non-positive, no idle object evictor thread will be
+     * run.
+     *
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized long getTimeBetweenEvictionRunsMillis() {
+        return _timeBetweenEvictionRunsMillis;
+    }
+
+    /**
+     * Sets the number of milliseconds to sleep between runs of the
+     * idle object evictor thread.
+     * When non-positive, no idle object evictor thread will be
+     * run.
+     *
+     * @see #getTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
+        if(_timeBetweenEvictionRunsMillis > 0 && timeBetweenEvictionRunsMillis <= 0) {
+            _evictor.cancel();
+            _evictor = null;
+            _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        } else if(_timeBetweenEvictionRunsMillis <= 0 && timeBetweenEvictionRunsMillis > 0) {
+            _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+            _evictor = new Evictor();
+            Thread t = new Thread(_evictor);
+            t.setDaemon(true);
+            t.start();
+        } else {
+            _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        }
+    }
+
+    /**
+     * Returns the number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     *
+     * @see #setNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public int getNumTestsPerEvictionRun() {
+        return _numTestsPerEvictionRun;
+    }
+
+    /**
+     * Sets the number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     * <p>
+     * When a negative value is supplied, <tt>ceil({@link #numIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
+     * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
+     * idle objects will be tested per run.
+     *
+     * @see #getNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+    }
+
+    /**
+     * Returns the minimum amount of time an object may sit idle in the pool
+     * before it is eligable for eviction by the idle object evictor
+     * (if any).
+     *
+     * @see #setMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized long getMinEvictableIdleTimeMillis() {
+        return _minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * Sets the minimum amount of time an object may sit idle in the pool
+     * before it is eligable for eviction by the idle object evictor
+     * (if any).
+     * When non-positive, no objects will be evicted from the pool
+     * due to idle time alone.
+     *
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @see #setTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public boolean getTestWhileIdle() {
+        return _testWhileIdle;
+    }
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @see #getTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public void setTestWhileIdle(boolean testWhileIdle) {
+        _testWhileIdle = testWhileIdle;
+    }
+
+    /**
+     * Sets my configuration.
+     * @see GenericObjectPool.Config
+     */
+    public synchronized void setConfig(GenericObjectPool.Config conf) {
+        setMaxIdle(conf.maxIdle);
+        setMaxActive(conf.maxActive);
+        setMaxWait(conf.maxWait);
+        setWhenExhaustedAction(conf.whenExhaustedAction);
+        setTestOnBorrow(conf.testOnBorrow);
+        setTestOnReturn(conf.testOnReturn);
+        setTestWhileIdle(conf.testWhileIdle);
+        setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
+        setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
+        setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
+        notifyAll();
+    }
+
+    //-- ObjectPool methods ------------------------------------------
+
+    public synchronized Object borrowObject() {
+        long starttime = System.currentTimeMillis();
+        for(;;) {
+            ObjectTimestampPair pair = null;
+            // if there are any sleeping, just grab one of those
+            try {
+                pair = (ObjectTimestampPair)(_pool.removeFirst());
+            } catch(NoSuchElementException e) { /* ignored */
+            }
+            // otherwise
+            if(null == pair) {
+                // check if we can create one
+                // (note we know that the num sleeping is 0, else we wouldn't be here)
+                if(_maxActive > 0 && _numActive < _maxActive) {
+                    Object obj = _factory.makeObject();
+                    pair = new ObjectTimestampPair(obj);
+                } else {
+                    // the pool is exhausted
+                    switch(_whenExhaustedAction) {
+                        case WHEN_EXHAUSTED_GROW:
+                            Object obj = _factory.makeObject();
+                            pair = new ObjectTimestampPair(obj);
+                            break;
+                        case WHEN_EXHAUSTED_FAIL:
+                            throw new NoSuchElementException();
+                        case WHEN_EXHAUSTED_BLOCK:
+                            try {
+                                if(_maxWait <= 0) {
+                                    wait();
+                                } else {
+                                    wait(_maxWait);
+                                }
+                            } catch(InterruptedException e) {
+                                // ignored
+                            }
+                            if(_maxWait > 0 && ((System.currentTimeMillis() - starttime) >= _maxWait)) {
+                                throw new NoSuchElementException("Timeout waiting for idle object");
+                            } else {
+                                continue; // keep looping
+                            }
+                        default:
+                            throw new IllegalArgumentException("whenExhaustedAction " + _whenExhaustedAction + " not recognized.");
+                    }
+                }
+            }
+            _factory.activateObject(pair.value);
+            if(_testOnBorrow && !_factory.validateObject(pair.value)) {
+                try {
+                    _factory.passivateObject(pair.value);
+                } catch(Exception e) {
+                    ; // ignored, we're throwing it out anyway
+                }
+                _factory.destroyObject(pair.value);
+            } else {
+                _numActive++;
+                return pair.value;
+            }
+        }
+    }
+
+    public synchronized void clear() throws UnsupportedOperationException {
+        Iterator it = _pool.iterator();
+        while(it.hasNext()) {
+            _factory.destroyObject(((ObjectTimestampPair)(it.next())).value);
+            it.remove();
+        }
+        _pool.clear();
+        notifyAll(); // num sleeping has changed
+    }
+
+    public int numActive() {
+        return _numActive;
+    }
+
+    public int numIdle() {
+        return _pool.size();
+    }
+
+    public synchronized void returnObject(Object obj) {
+        _numActive--;
+        if(_maxIdle > 0 && (_pool.size() >= _maxIdle || (_testOnReturn && !_factory.validateObject(obj)))) {
+            try {
+                _factory.passivateObject(obj);
+            } catch(Exception e) {
+                ; // ignored, we're throwing it out anway
+            }
+            _factory.destroyObject(obj);
+        } else {
+            try {
+                _factory.passivateObject(obj);
+                _pool.addFirst(new ObjectTimestampPair(obj));
+            } catch(Exception e) {
+                _factory.destroyObject(obj);
+            }
+        }
+        notifyAll(); // _numActive has changed
+    }
+
+    synchronized public void close() {
+        clear();
+        _pool = null;
+        _factory = null;
+        if(null != _evictor) {
+            _evictor.cancel();
+            _evictor = null;
+        }
+    }
+
+    synchronized public void setFactory(PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
+        if(0 < numActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+
+    //--- package methods --------------------------------------------
+
+    synchronized String debugInfo() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Active: ").append(numActive()).append("\n");
+        buf.append("Idle: ").append(numIdle()).append("\n");
+        buf.append("Idle Objects:\n");
+        Iterator it = _pool.iterator();
+        long time = System.currentTimeMillis();
+        while(it.hasNext()) {
+            ObjectTimestampPair pair = (ObjectTimestampPair)(it.next());
+            buf.append("\t").append(pair.value).append("\t").append(time - pair.tstamp).append("\n");
+        }
+        return buf.toString();
+    }
+
+    //--- inner classes ----------------------------------------------
+
+    /**
+     * A simple "struct" encapsulating an object instance and a timestamp.
+     */
+    class ObjectTimestampPair {
+        Object value;
+        long tstamp;
+
+        ObjectTimestampPair(Object val) {
+            value = val;
+            tstamp = System.currentTimeMillis();
+        }
+
+        ObjectTimestampPair(Object val, long time) {
+            value = val;
+            tstamp = time;
+        }
+    }
+
+    /**
+     * The idle object evictor thread.
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    class Evictor implements Runnable {
+        protected boolean _cancelled = false;
+
+        void cancel() {
+            _cancelled = true;
+        }
+
+        public void run() {
+            CursorableLinkedList.Cursor cursor = null;
+            while(!_cancelled) {
+                long sleeptime = 0L;
+                synchronized(GenericObjectPool.this) {
+                    sleeptime = _timeBetweenEvictionRunsMillis;
+                }
+                try {
+                    Thread.currentThread().sleep(_timeBetweenEvictionRunsMillis);
+                } catch(Exception e) {
+                    ; // ignored
+                }
+                try {
+                    synchronized(GenericObjectPool.this) {
+                        if(!_pool.isEmpty()) {
+                            if(null == cursor) {
+                                cursor = (CursorableLinkedList.Cursor)(_pool.cursor(_pool.size()));
+                            } else if(!cursor.hasPrevious()) {
+                                cursor.close();
+                                cursor = (CursorableLinkedList.Cursor)(_pool.cursor(_pool.size()));
+                            }
+                            for(int i=0,m=getNumTests();i<m;i++) {
+                                if(!cursor.hasPrevious()) {
+                                    cursor.close();
+                                    cursor = (CursorableLinkedList.Cursor)(_pool.cursor(_pool.size()));
+                                } else {
+                                    ObjectTimestampPair pair = (ObjectTimestampPair)(cursor.previous());
+                                    if(System.currentTimeMillis() - pair.tstamp > _minEvictableIdleTimeMillis) {
+                                        try {
+                                            cursor.remove();
+                                            _factory.destroyObject(pair.value);
+                                        } catch(Exception e) {
+                                            ; // ignored
+                                        }
+                                    } else if(_testWhileIdle) {
+                                        boolean active = false;
+                                        try {
+                                            _factory.activateObject(pair.value);
+                                            active = true;
+                                        } catch(Exception e) {
+                                            cursor.remove();
+                                            try {
+                                                _factory.passivateObject(pair.value);
+                                            } catch(Exception ex) {
+                                                ; // ignored
+                                            }
+                                            _factory.destroyObject(pair.value);
+                                        }
+                                        if(active) {
+                                            if(!_factory.validateObject(pair.value)) {
+                                                cursor.remove();
+                                                try {
+                                                    _factory.passivateObject(pair.value);
+                                                } catch(Exception ex) {
+                                                    ; // ignored
+                                                }
+                                                _factory.destroyObject(pair.value);
+                                            } else {
+                                                try {
+                                                    _factory.passivateObject(pair.value);
+                                                } catch(Exception e) {
+                                                    cursor.remove();
+                                                    _factory.destroyObject(pair.value);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } catch(Exception e) {
+                    // ignored
+                }
+            }
+            if(null != cursor) {
+                cursor.close();
+            }
+        }
+
+        private int getNumTests() {
+            if(_numTestsPerEvictionRun >= 0) {
+                return _numTestsPerEvictionRun;
+            } else {
+                return(int)(Math.ceil((double)_pool.size()/Math.abs((double)_numTestsPerEvictionRun)));
+            }
+        }
+    }
+
+    /**
+     * A simple "struct" encapsulating the
+     * configuration information for a {@link GenericObjectPool}.
+     * @see GenericObjectPool#GenericObjectPool(org.apache.commons.pool.PoolableObjectFactory,org.apache.commons.pool.impl.GenericObjectPool.Config)
+     * @see GenericObjectPool#setConfig
+     */
+    public static class Config {
+        public int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
+        public int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
+        public long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
+        public byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+        public boolean testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
+        public boolean testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
+        public boolean testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
+        public long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+        public int numTestsPerEvictionRun =  GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+        public long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    }
+
+    //--- protected attributes ---------------------------------------
+
+    /**
+     * The cap on the number of idle instances in the pool.
+     * @see #setMaxIdle
+     * @see #getMaxIdle
+     */
+    protected int _maxIdle = DEFAULT_MAX_IDLE;
+
+    /**
+     * The cap on the total number of active instances from the pool.
+     * @see #setMaxActive
+     * @see #getMaxActive
+     */
+    protected int _maxActive = DEFAULT_MAX_ACTIVE;
+
+    /**
+     * The maximum amount of time (in millis) the
+     * {@link #borrowObject} method should block before throwing
+     * an exception when the pool is exhausted and the
+     * {@link #getWhenExhaustedAction "when exhausted" action} is
+     * {@link #WHEN_EXHAUSTED_BLOCK}.
+     *
+     * When less than 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #setMaxWait
+     * @see #getMaxWait
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #setWhenExhaustedAction
+     * @see #getWhenExhaustedAction
+     */
+    protected long _maxWait = DEFAULT_MAX_WAIT;
+
+    /**
+     * The action to take when the {@link #borrowObject} method
+     * is invoked when the pool is exhausted (the maximum number
+     * of "active" objects has been reached).
+     *
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #WHEN_EXHAUSTED_FAIL
+     * @see #WHEN_EXHAUSTED_GROW
+     * @see #DEFAULT_WHEN_EXHAUSTED_ACTION
+     * @see #setWhenExhaustedAction
+     * @see #getWhenExhaustedAction
+     */
+    protected byte _whenExhaustedAction = DEFAULT_WHEN_EXHAUSTED_ACTION;
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned by the {@link #borrowObject}
+     * method.  If the object fails to validate,
+     * it will be dropped from the pool, and we will attempt
+     * to borrow another.
+     *
+     * @see #setTestOnBorrow
+     * @see #getTestOnBorrow
+     */
+    protected boolean _testOnBorrow = DEFAULT_TEST_ON_BORROW;
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #getTestOnReturn
+     * @see #setTestOnReturn
+     */
+    protected boolean _testOnReturn = DEFAULT_TEST_ON_RETURN;
+
+    /**
+     * When <tt>true</tt>, objects will be
+     * {@link PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @see #setTestWhileIdle
+     * @see #getTestWhileIdle
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    protected boolean _testWhileIdle = DEFAULT_TEST_WHILE_IDLE;
+
+    /**
+     * The number of milliseconds to sleep between runs of the
+     * idle object evictor thread.
+     * When non-positive, no idle object evictor thread will be
+     * run.
+     *
+     * @see #setTimeBetweenEvictionRunsMillis
+     * @see #getTimeBetweenEvictionRunsMillis
+     */
+    protected long _timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+
+    /**
+     * The number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     * <p>
+     * When a negative value is supplied, <tt>ceil({@link #numIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
+     * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
+     * idle objects will be tested per run.
+     *
+     * @see #setNumTestsPerEvictionRun
+     * @see #getNumTestsPerEvictionRun
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    protected int _numTestsPerEvictionRun =  DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+
+    /**
+     * The minimum amount of time an object may sit idle in the pool
+     * before it is eligable for eviction by the idle object evictor
+     * (if any).
+     * When non-positive, no objects will be evicted from the pool
+     * due to idle time alone.
+     *
+     * @see #setMinEvictableIdleTimeMillis
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    protected long _minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+    /** My pool. */
+    protected CursorableLinkedList _pool = null;
+
+    /** My {@link PoolableObjectFactory}. */
+    protected PoolableObjectFactory _factory = null;
+
+    /**
+     * The number of objects {@link #borrowObject} borrowed
+     * from the pool, but not yet returned.
+     */
+    protected int _numActive = 0;
+
+    /**
+     * My idle object eviction thread, if any.
+     */
+    protected Evictor _evictor = null;
+}
diff --git a/src/java/org/apache/commons/pool/impl/GenericObjectPoolFactory.java b/src/java/org/apache/commons/pool/impl/GenericObjectPoolFactory.java
new file mode 100644
index 0000000..496b8e7
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericObjectPoolFactory.java
@@ -0,0 +1,135 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/GenericObjectPoolFactory.java,v 1.1 2001/04/14 16:41:46 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:46 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+
+/**
+ * A factory for creating {@link GenericObjectPool} instances.
+ *
+ * @see GenericObjectPool
+ * @see ObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: GenericObjectPoolFactory.java,v 1.1 2001/04/14 16:41:46 rwaldhoff Exp $
+ */
+public class GenericObjectPoolFactory implements ObjectPoolFactory {
+    public GenericObjectPoolFactory(PoolableObjectFactory factory) {
+        this(factory,GenericObjectPool.DEFAULT_MAX_ACTIVE,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, GenericObjectPool.Config config) {
+        this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive) {
+        this(factory,maxActive,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericObjectPool.DEFAULT_TEST_ON_BORROW,GenericObjectPool.DEFAULT_TEST_ON_RETURN,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,testOnBorrow,testOnReturn,GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
+    }
+
+    public GenericObjectPoolFactory(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        _maxIdle = maxIdle;
+        _maxActive = maxActive;
+        _maxWait = maxWait;
+        _whenExhaustedAction = whenExhaustedAction;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _testWhileIdle = testWhileIdle;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _factory = factory;
+    }
+
+    public ObjectPool createPool() {
+        return new GenericObjectPool(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle);
+    }
+
+    protected int _maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
+    protected int _maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
+    protected long _maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
+    protected byte _whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+    protected boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
+    protected boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
+    protected boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
+    protected long _timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+    protected int _numTestsPerEvictionRun =  GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+    protected long _minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    protected PoolableObjectFactory _factory = null;
+
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/StackKeyedObjectPool.java b/src/java/org/apache/commons/pool/impl/StackKeyedObjectPool.java
new file mode 100644
index 0000000..57ac45e
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackKeyedObjectPool.java
@@ -0,0 +1,331 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/StackKeyedObjectPool.java,v 1.1 2001/04/14 16:41:50 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:50 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+import java.util.HashMap;
+import java.util.Stack;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * A simple, {@link java.util.Stack Stack}-based {@link KeyedObjectPool} implementation.
+ * <p>
+ * Given a {@link KeyedPoolableObjectFactory}, this class will maintain
+ * a simple pool of instances.  A finite number of "sleeping"
+ * or inactive instances is enforced, but when the pool is
+ * empty, new instances are created to support the new load.
+ * Hence this class places no limit on the number of "active"
+ * instances created by the pool, but is quite useful for
+ * re-using <tt>Object</tt>s without introducing
+ * artificial limits.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: StackKeyedObjectPool.java,v 1.1 2001/04/14 16:41:50 rwaldhoff Exp $
+ */
+public class StackKeyedObjectPool implements KeyedObjectPool {
+    /**
+     * Create a new pool using
+     * no factory. Clients must first populate the pool
+     * using {@link #returnObject(java.lang.Object,java.lang.Object)}
+     * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
+     */
+    public StackKeyedObjectPool() {
+        this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using
+     * no factory. Clients must first populate the pool
+     * using {@link #returnObject(java.lang.Object,java.lang.Object)}
+     * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
+     */
+    public StackKeyedObjectPool(int max) {
+        this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using
+     * no factory. Clients must first populate the pool
+     * using {@link #returnObject(java.lang.Object,java.lang.Object)}
+     * before they can be {@link #borrowObject(java.lang.Object) borrowed}.
+     */
+    public StackKeyedObjectPool(int max, int init) {
+        this((KeyedPoolableObjectFactory)null,max,init);
+    }
+
+    /**
+     * Create a new <tt>SimpleKeyedObjectPool</tt> using
+     * the specified <i>factory</i> to create new instances.
+     *
+     * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
+     */
+    public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
+        this(factory,DEFAULT_MAX_SLEEPING);
+    }
+
+    /**
+     * Create a new <tt>SimpleKeyedObjectPool</tt> using
+     * the specified <i>factory</i> to create new instances.
+     * capping the number of "sleeping" instances to <i>max</i>
+     *
+     * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
+     * @param max cap on the number of "sleeping" instances in the pool
+     */
+    public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) {
+        this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new <tt>SimpleKeyedObjectPool</tt> using
+     * the specified <i>factory</i> to create new instances.
+     * capping the number of "sleeping" instances to <i>max</i>,
+     * and initially allocating a container capable of containing
+     * at least <i>init</i> instances.
+     *
+     * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
+     * @param max cap on the number of "sleeping" instances in the pool
+     * @param init initial size of the pool (this specifies the size of the container,
+     *             it does not cause the pool to be pre-populated.)
+     */
+    public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) {
+        _factory = factory;
+        _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
+        _initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
+        _pools = new HashMap();
+        _activeCount = new HashMap();
+    }
+
+    public synchronized Object borrowObject(Object key) {
+        Object obj = null;
+        Stack stack = (Stack)(_pools.get(key));
+        if(null == stack) {
+            stack = new Stack();
+            stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
+            _pools.put(key,stack);
+            _activeCount.put(key,new Integer(0));
+        }
+        try {
+            obj = stack.pop();
+            _totIdle--;
+        } catch(Exception e) {
+            if(null == _factory) {
+                throw new NoSuchElementException();
+            } else {
+                obj = _factory.makeObject(key);
+            }
+        }
+        if(null != obj && null != _factory) {
+            _factory.activateObject(key,obj);
+        }
+        _totActive++;
+        Integer old = (Integer)(_activeCount.get(key));
+        _activeCount.put(key,new Integer(old.intValue() + 1));
+        return obj;
+    }
+
+    public synchronized void returnObject(Object key, Object obj) {
+        _totActive--;
+        if(null == _factory || _factory.validateObject(key,obj)) {
+            Stack stack = (Stack)(_pools.get(key));
+            if(null == stack) {
+                stack = new Stack();
+                stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
+                _pools.put(key,stack);
+                _activeCount.put(key,new Integer(1));
+            }
+            if(null != _factory) {
+                try {
+                    _factory.passivateObject(key,obj);
+                } catch(Exception e) {
+                    _factory.destroyObject(key,obj);
+                    return;
+                }
+            }
+            if(stack.size() < _maxSleeping) {
+                stack.push(obj);
+                _totIdle++;
+            } else {
+                if(null != _factory) {
+                    _factory.destroyObject(key,obj);
+                }
+            }
+        } else {
+            if(null != _factory) {
+                _factory.destroyObject(key,obj);
+            }
+        }
+        Integer old = (Integer)(_activeCount.get(key));
+        _activeCount.put(key,new Integer(old.intValue() - 1));
+    }
+
+    public int numIdle() {
+        return _totIdle;
+    }
+
+    public int numActive() {
+        return _totActive;
+    }
+
+    public int numActive(Object key) {
+        try {
+            return ((Integer)_activeCount.get(key)).intValue();
+        } catch(NoSuchElementException e) {
+            return 0;
+        } catch(NullPointerException e) {
+            return 0;
+        }
+    }
+
+    public synchronized int numIdle(Object key) {
+        try {
+            return((Stack)(_pools.get(key))).size();
+        } catch(Exception e) {
+            return 0;
+        }
+    }
+
+    public synchronized void clear() throws UnsupportedOperationException {
+        Iterator it = _pools.keySet().iterator();
+        while(it.hasNext()) {
+            clear(it.next());
+        }
+        _totIdle = 0;
+        _pools.clear();
+        _activeCount.clear();
+    }
+
+    public synchronized void clear(Object key) throws UnsupportedOperationException {
+        Stack stack = (Stack)(_pools.remove(key));
+        if(null == stack) {
+            return;
+        } else {
+            Enumeration enum = stack.elements();
+            while(enum.hasMoreElements()) {
+                _factory.destroyObject(key,enum.nextElement());
+            }
+            _totIdle -= stack.size();
+            _activeCount.put(key,new Integer(0));
+            stack.clear();
+        }
+    }
+
+    public synchronized String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append(getClass().getName());
+        buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
+        Iterator it = _pools.keySet().iterator();
+        while(it.hasNext()) {
+            Object key = it.next();
+            buf.append(" |").append(key).append("|=");
+            Stack s = (Stack)(_pools.get(key));
+            buf.append(s.size());
+        }
+        return buf.toString();
+    }
+
+    synchronized public void close() {
+        clear();
+        _pools = null;
+        _factory = null;
+        _activeCount = null;
+    }
+
+    synchronized public void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
+        if(0 < numActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+
+
+    /** The default cap on the number of "sleeping" instances in the pool. */
+    protected static final int DEFAULT_MAX_SLEEPING  = 8;
+
+    /**
+     * The default initial size of the pool
+     * (this specifies the size of the container, it does not
+     * cause the pool to be pre-populated.)
+     */
+    protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
+
+    /** My named-set of pools. */
+    protected HashMap _pools = null;
+
+    /** My {@link KeyedPoolableObjectFactory}. */
+    protected KeyedPoolableObjectFactory _factory = null;
+
+    /** The cap on the number of "sleeping" instances in <i>each</i> pool. */
+    protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
+
+    /** The initial capacity of each pool. */
+    protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
+
+    protected int _totActive = 0;
+    protected int _totIdle = 0;
+
+    protected HashMap _activeCount = null;
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/impl/StackKeyedObjectPoolFactory.java b/src/java/org/apache/commons/pool/impl/StackKeyedObjectPoolFactory.java
new file mode 100644
index 0000000..75682c5
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackKeyedObjectPoolFactory.java
@@ -0,0 +1,113 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/StackKeyedObjectPoolFactory.java,v 1.1 2001/04/14 16:41:52 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:52 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+import java.util.Stack;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+
+/**
+ * A factory for creating {@link StackKeyedObjectPool} instances.
+ *
+ * @see StackKeyedObjectPool
+ * @see KeyedObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: StackKeyedObjectPoolFactory.java,v 1.1 2001/04/14 16:41:52 rwaldhoff Exp $
+ */
+public class StackKeyedObjectPoolFactory implements KeyedObjectPoolFactory {
+    public StackKeyedObjectPoolFactory() {
+        this((KeyedPoolableObjectFactory)null,StackKeyedObjectPool.DEFAULT_MAX_SLEEPING,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackKeyedObjectPoolFactory(int max) {
+        this((KeyedPoolableObjectFactory)null,max,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackKeyedObjectPoolFactory(int max, int init) {
+        this((KeyedPoolableObjectFactory)null,max,init);
+    }
+
+    public StackKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory) {
+        this(factory,StackKeyedObjectPool.DEFAULT_MAX_SLEEPING,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int max) {
+        this(factory,max,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, int max, int init) {
+        _factory = factory;
+        _maxSleeping = max;
+        _initCapacity = init;
+    }
+
+    public KeyedObjectPool createPool() {
+        return new StackKeyedObjectPool(_factory,_maxSleeping,_initCapacity);
+    }
+
+    protected KeyedPoolableObjectFactory _factory = null;
+    protected int _maxSleeping = StackKeyedObjectPool.DEFAULT_MAX_SLEEPING;
+    protected int _initCapacity = StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY;
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/StackObjectPool.java b/src/java/org/apache/commons/pool/impl/StackObjectPool.java
new file mode 100644
index 0000000..44faebe
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackObjectPool.java
@@ -0,0 +1,253 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/StackObjectPool.java,v 1.1 2001/04/14 16:41:56 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:56 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+import java.util.Stack;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+
+/**
+ * A simple, {@link java.util.Stack Stack}-based {@link ObjectPool} implementation.
+ * <p>
+ * Given a {@link PoolableObjectFactory}, this class will maintain
+ * a simple pool of instances.  A finite number of "sleeping"
+ * or idle instances is enforced, but when the pool is
+ * empty, new instances are created to support the new load.
+ * Hence this class places no limit on the number of "active"
+ * instances created by the pool, but is quite useful for
+ * re-using <tt>Object</tt>s without introducing
+ * artificial limits.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: StackObjectPool.java,v 1.1 2001/04/14 16:41:56 rwaldhoff Exp $
+ */
+public class StackObjectPool implements ObjectPool {
+    /**
+     * Create a new pool using
+     * no factory. Clients must first populate the pool
+     * using {@link #returnObject(java.lang.Object)}
+     * before they can be {@link #borrowObject borrowed}.
+     */
+    public StackObjectPool() {
+        this((PoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using
+     * no factory. Clients must first populate the pool
+     * using {@link #returnObject(java.lang.Object)}
+     * before they can be {@link #borrowObject borrowed}.
+     */
+    public StackObjectPool(int max) {
+        this((PoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using
+     * no factory. Clients must first populate the pool
+     * using {@link #returnObject(java.lang.Object)}
+     * before they can be {@link #borrowObject borrowed}.
+     */
+    public StackObjectPool(int max, int init) {
+        this((PoolableObjectFactory)null,max,init);
+    }
+
+    /**
+     * Create a new <tt>StackObjectPool</tt> using
+     * the specified <i>factory</i> to create new instances.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to populate the pool
+     */
+    public StackObjectPool(PoolableObjectFactory factory) {
+        this(factory,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new <tt>SimpleObjectPool</tt> using
+     * the specified <i>factory</i> to create new instances,
+     * capping the number of "sleeping" instances to <i>max</i>
+     *
+     * @param factory the {@link PoolableObjectFactory} used to populate the pool
+     * @param max cap on the number of "sleeping" instances in the pool
+     */
+    public StackObjectPool(PoolableObjectFactory factory, int max) {
+        this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new <tt>SimpleObjectPool</tt> using
+     * the specified <i>factory</i> to create new instances,
+     * capping the number of "sleeping" instances to <i>max</i>,
+     * and initially allocating a container capable of containing
+     * at least <i>init</i> instances.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to populate the pool
+     * @param max cap on the number of "sleeping" instances in the pool
+     * @param init initial size of the pool (this specifies the size of the container,
+     *             it does not cause the pool to be pre-populated.)
+     */
+    public StackObjectPool(PoolableObjectFactory factory, int max, int init) {
+        _factory = factory;
+        _maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
+        int initcapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
+        _pool = new Stack();
+        _pool.ensureCapacity( initcapacity > _maxSleeping ? _maxSleeping : initcapacity);
+    }
+
+    public synchronized Object borrowObject() {
+        Object obj = null;
+        try {
+            obj = _pool.pop();
+        } catch(Exception e) {
+            if(null == _factory) {
+                throw new NoSuchElementException();
+            } else {
+                obj = _factory.makeObject();
+            }
+        }
+        if(null != _factory && null != obj) {
+            _factory.activateObject(obj);
+        }
+        _numActive++;
+        return obj;
+    }
+
+    public synchronized void returnObject(Object obj) {
+        _numActive--;
+        if(null == _factory || _factory.validateObject(obj)) {
+            if(null != _factory) {
+                try {
+                    _factory.passivateObject(obj);
+                } catch(Exception e) {
+                    _factory.destroyObject(obj);
+                    return;
+                }
+            }
+            if(_pool.size() < _maxSleeping) {
+                _pool.push(obj);
+            } else {
+                if(null != _factory) {
+                    _factory.destroyObject(obj);
+                }
+            }
+        } else {
+            if(null != _factory) {
+                _factory.destroyObject(obj);
+            }
+        }
+    }
+
+    public int numIdle() {
+        return _pool.size();
+    }
+
+    public int numActive() {
+        return _numActive;
+    }
+
+    public synchronized void clear() throws UnsupportedOperationException {
+        if(null != _factory) {
+            Enumeration enum = _pool.elements();
+            while(enum.hasMoreElements()) {
+                _factory.destroyObject(enum.nextElement());
+            }
+        }
+        _pool.clear();
+    }
+
+    synchronized public void close() {
+        clear();
+        _pool = null;
+        _factory = null;
+    }
+
+    synchronized public void setFactory(PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
+        if(0 < numActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+
+    /** The default cap on the number of "sleeping" instances in the pool. */
+    protected static final int DEFAULT_MAX_SLEEPING  = 8;
+
+    /**
+     * The default initial size of the pool
+     * (this specifies the size of the container, it does not
+     * cause the pool to be pre-populated.)
+     */
+    protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
+
+    /** My pool. */
+    protected Stack _pool = null;
+
+    /** My {@link PoolableObjectFactory}. */
+    protected PoolableObjectFactory _factory = null;
+
+    /** The cap on the number of "sleeping" instances in the pool. */
+    protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
+
+    protected int _numActive = 0;
+}
\ No newline at end of file
diff --git a/src/java/org/apache/commons/pool/impl/StackObjectPoolFactory.java b/src/java/org/apache/commons/pool/impl/StackObjectPoolFactory.java
new file mode 100644
index 0000000..c2aa276
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackObjectPoolFactory.java
@@ -0,0 +1,113 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/java/org/apache/commons/pool/impl/StackObjectPoolFactory.java,v 1.1 2001/04/14 16:41:58 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:41:58 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.*;
+import java.util.Stack;
+import java.util.NoSuchElementException;
+import java.util.Enumeration;
+
+/**
+ * A factory for creating {@link StackObjectPool} instances.
+ *
+ * @see StackObjectPool
+ * @see KeyedObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: StackObjectPoolFactory.java,v 1.1 2001/04/14 16:41:58 rwaldhoff Exp $
+ */
+public class StackObjectPoolFactory implements ObjectPoolFactory {
+    public StackObjectPoolFactory() {
+        this((PoolableObjectFactory)null,StackObjectPool.DEFAULT_MAX_SLEEPING,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackObjectPoolFactory(int max) {
+        this((PoolableObjectFactory)null,max,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackObjectPoolFactory(int max, int init) {
+        this((PoolableObjectFactory)null,max,init);
+    }
+
+    public StackObjectPoolFactory(PoolableObjectFactory factory) {
+        this(factory,StackObjectPool.DEFAULT_MAX_SLEEPING,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackObjectPoolFactory(PoolableObjectFactory factory, int max) {
+        this(factory,max,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    public StackObjectPoolFactory(PoolableObjectFactory factory, int max, int init) {
+        _factory = factory;
+        _maxSleeping = max;
+        _initCapacity = init;
+    }
+
+    public ObjectPool createPool() {
+        return new StackObjectPool(_factory,_maxSleeping,_initCapacity);
+    }
+
+    protected PoolableObjectFactory _factory = null;
+    protected int _maxSleeping = StackObjectPool.DEFAULT_MAX_SLEEPING;
+    protected int _initCapacity = StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY;
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/package.html b/src/java/org/apache/commons/pool/impl/package.html
new file mode 100644
index 0000000..2cb4408
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/package.html
@@ -0,0 +1,40 @@
+<!-- $Id: package.html,v 1.1 2001/04/14 16:41:59 rwaldhoff Exp $ -->
+<html>
+   <head>
+      <title>Package Documentation for org.apache.commons.pool.impl</title>
+   </head>
+   <body>
+      <p>
+         Object pooling API implementations.
+      </p>
+      <p>
+         {@link org.apache.commons.pool.impl.StackObjectPool StackObjectPool}
+         ({@link org.apache.commons.pool.impl.StackKeyedObjectPool StackKeyedObjectPool})
+         provides a simple, {@link java.util.Stack Stack}-based
+         implementation of {@link org.apache.commons.pool.ObjectPool ObjectPool}
+         ({@link org.apache.commons.pool.KeyedObjectPool KeyedObjectPool}).
+         {@link org.apache.commons.pool.impl.StackObjectPoolFactory StackObjectPoolFactory}
+         ({@link org.apache.commons.pool.impl.StackKeyedObjectPoolFactory StackKeyedObjectPoolFactory})
+         provides an implementation of the
+         {@link org.apache.commons.pool.ObjectPoolFactory ObjectPoolFactory}
+         ({@link org.apache.commons.pool.KeyedObjectPoolFactory KeyedObjectPoolFactory})
+         factory interface for this class.
+      </p>
+      <p>
+         {@link org.apache.commons.pool.impl.GenericObjectPool GenericObjectPool}
+         ({@link org.apache.commons.pool.impl.GenericKeyedObjectPool GenericKeyedObjectPool})
+         provides a more robust (but also more complicated)
+         implementation of {@link org.apache.commons.pool.ObjectPool ObjectPool}
+         ({@link org.apache.commons.pool.KeyedObjectPool KeyedObjectPool}).
+         {@link org.apache.commons.pool.impl.GenericObjectPoolFactory GenericObjectPoolFactory}
+         ({@link org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory GenericKeyedObjectPoolFactory})
+         provides an implementation of the
+         {@link org.apache.commons.pool.ObjectPoolFactory ObjectPoolFactory}
+         ({@link org.apache.commons.pool.KeyedObjectPoolFactory KeyedObjectPoolFactory})
+         factory interface for this class.
+      </p>
+      <p>
+         See also the {@link org.apache.commons.pool} package.
+      </p>
+   </body>
+</html>
diff --git a/src/java/org/apache/commons/pool/package.html b/src/java/org/apache/commons/pool/package.html
new file mode 100644
index 0000000..78e31c5
--- /dev/null
+++ b/src/java/org/apache/commons/pool/package.html
@@ -0,0 +1,40 @@
+<!-- $Id: package.html,v 1.1 2001/04/14 16:40:54 rwaldhoff Exp $ -->
+<html>
+   <head>
+      <title>Package Documentation for org.apache.commons.pool</title>
+   </head>
+   <body>
+      <p>
+         Generic object pooling API.
+      </p>
+      <p>
+         The pool package seperates the way in which objects are pooled
+         from the way in which they are created.  To use the pooling API, simply
+         associate a
+         {@link org.apache.commons.pool.PoolableObjectFactory PoolableObjectFactory}
+         ({@link org.apache.commons.pool.KeyedPoolableObjectFactory KeyedPoolableObjectFactory})
+         implementation with an
+         {@link org.apache.commons.pool.ObjectPool ObjectPool}
+         ({@link org.apache.commons.pool.KeyedObjectPool KeyedObjectPool})
+         implementation.
+      </p>
+      <p>
+         {@link org.apache.commons.pool.ObjectPool ObjectPool} defines a simple
+         object pooling interface.
+         {@link org.apache.commons.pool.KeyedObjectPool KeyedObjectPool} defines a
+         similiar interface for pooling "typed" or "keyed" objects.
+      </p>
+      <p>
+         The {@link org.apache.commons.pool.PoolableObjectFactory PoolableObjectFactory}
+         interface defines lifecycle methods for instances to be returned from an
+         {@link org.apache.commons.pool.ObjectPool ObjectPool}.
+         The {@link org.apache.commons.pool.KeyedPoolableObjectFactory KeyedPoolableObjectFactory}
+         interface defines similiar methods for instances to be returned from a
+         {@link org.apache.commons.pool.KeyedObjectPool KeyedObjectPool}.
+      </p>
+      <p>
+         The {@link org.apache.commons.pool.impl} package contains several simple
+         pool implementations.
+      </p>
+   </body>
+</html>
diff --git a/src/test/org/apache/commons/pool/TestAll.java b/src/test/org/apache/commons/pool/TestAll.java
new file mode 100644
index 0000000..171dfc5
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestAll.java
@@ -0,0 +1,85 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/test/org/apache/commons/pool/TestAll.java,v 1.1 2001/04/14 16:42:01 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:42:01 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool;
+
+import junit.framework.*;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Id: TestAll.java,v 1.1 2001/04/14 16:42:01 rwaldhoff Exp $
+ */
+public class TestAll extends TestCase {
+    public TestAll(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTest(org.apache.commons.pool.impl.TestAll.suite());
+        return suite;
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestAll.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+}
diff --git a/src/test/org/apache/commons/pool/impl/TestAll.java b/src/test/org/apache/commons/pool/impl/TestAll.java
new file mode 100644
index 0000000..0793456
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestAll.java
@@ -0,0 +1,91 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/test/org/apache/commons/pool/impl/TestAll.java,v 1.1 2001/04/14 16:42:02 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:42:02 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import junit.framework.*;
+
+/**
+ * JUnit test suite for the {@link org.apache.commons.pool.impl}
+ * package.
+ *
+ * @author Rodney Waldhoff
+ * @version $Id: TestAll.java,v 1.1 2001/04/14 16:42:02 rwaldhoff Exp $
+ */
+public class TestAll extends TestCase {
+    public TestAll(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTest(TestStackObjectPool.suite());
+        suite.addTest(TestStackKeyedObjectPool.suite());
+        suite.addTest(TestGenericObjectPool.suite());
+        suite.addTest(TestGenericKeyedObjectPool.suite());
+        return suite;
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestAll.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+}
diff --git a/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPool.java b/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPool.java
new file mode 100644
index 0000000..c9ab6e6
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPool.java
@@ -0,0 +1,419 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPool.java,v 1.1 2001/04/14 16:42:08 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:42:08 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import junit.framework.*;
+import org.apache.commons.pool.*;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Id: TestGenericKeyedObjectPool.java,v 1.1 2001/04/14 16:42:08 rwaldhoff Exp $
+ */
+public class TestGenericKeyedObjectPool extends TestCase {
+    public TestGenericKeyedObjectPool(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TestGenericKeyedObjectPool.class);
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestGenericKeyedObjectPool.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+
+    private GenericKeyedObjectPool pool = null;
+
+    public void setUp() {
+        pool = new GenericKeyedObjectPool(
+            new KeyedPoolableObjectFactory()  {
+                int counter = 0;
+                public Object makeObject(Object key) { return String.valueOf(key) + String.valueOf(counter++); }
+                public void destroyObject(Object key, Object obj) { }
+                public boolean validateObject(Object key, Object obj) { return true; }
+                public void activateObject(Object key, Object obj) { }
+                public void passivateObject(Object key, Object obj) { }
+            }
+            );
+    }
+
+    public void testBorrow() {
+        Object obj0 = pool.borrowObject("");
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject("");
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+    }
+
+    public void testBorrowReturn() {
+        Object obj0 = pool.borrowObject("");
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject("");
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+        pool.returnObject("",obj2);
+        obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+        pool.returnObject("",obj1);
+        obj1 = pool.borrowObject("");
+        assertEquals("1",obj1);
+        pool.returnObject("",obj0);
+        pool.returnObject("",obj2);
+        obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+        obj0 = pool.borrowObject("");
+        assertEquals("0",obj0);
+    }
+
+    public void testNumActiveNumIdle() {
+        assertEquals(0,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj0 = pool.borrowObject("");
+        assertEquals(1,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj1 = pool.borrowObject("");
+        assertEquals(2,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        pool.returnObject("",obj1);
+        assertEquals(1,pool.numActive(""));
+        assertEquals(1,pool.numIdle(""));
+        pool.returnObject("",obj0);
+        assertEquals(0,pool.numActive(""));
+        assertEquals(2,pool.numIdle(""));
+    }
+
+    public void testNumActiveNumIdle2() {
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        assertEquals(0,pool.numActive("A"));
+        assertEquals(0,pool.numIdle("A"));
+        assertEquals(0,pool.numActive("B"));
+        assertEquals(0,pool.numIdle("B"));
+
+        Object objA0 = pool.borrowObject("A");
+        Object objB0 = pool.borrowObject("B");
+
+        assertEquals(2,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        assertEquals(1,pool.numActive("A"));
+        assertEquals(0,pool.numIdle("A"));
+        assertEquals(1,pool.numActive("B"));
+        assertEquals(0,pool.numIdle("B"));
+
+        Object objA1 = pool.borrowObject("A");
+        Object objB1 = pool.borrowObject("B");
+
+        assertEquals(4,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        assertEquals(2,pool.numActive("A"));
+        assertEquals(0,pool.numIdle("A"));
+        assertEquals(2,pool.numActive("B"));
+        assertEquals(0,pool.numIdle("B"));
+
+        pool.returnObject("A",objA0);
+        pool.returnObject("B",objB0);
+
+        assertEquals(2,pool.numActive());
+        assertEquals(2,pool.numIdle());
+        assertEquals(1,pool.numActive("A"));
+        assertEquals(1,pool.numIdle("A"));
+        assertEquals(1,pool.numActive("B"));
+        assertEquals(1,pool.numIdle("B"));
+
+        pool.returnObject("A",objA1);
+        pool.returnObject("B",objB1);
+
+        assertEquals(0,pool.numActive());
+        assertEquals(4,pool.numIdle());
+        assertEquals(0,pool.numActive("A"));
+        assertEquals(2,pool.numIdle("A"));
+        assertEquals(0,pool.numActive("B"));
+        assertEquals(2,pool.numIdle("B"));
+    }
+
+    public void testClear() {
+        assertEquals(0,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj0 = pool.borrowObject("");
+        Object obj1 = pool.borrowObject("");
+        assertEquals(2,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        pool.returnObject("",obj1);
+        pool.returnObject("",obj0);
+        assertEquals(0,pool.numActive(""));
+        assertEquals(2,pool.numIdle(""));
+        pool.clear("");
+        assertEquals(0,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+    }
+
+    public void testMaxIdle() {
+        pool.setMaxActive(100);
+        pool.setMaxIdle(8);
+        Object[] active = new Object[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        assertEquals(100,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        for(int i=0;i<100;i++) {
+            pool.returnObject("",active[i]);
+            assertEquals(99 - i,pool.numActive(""));
+            assertEquals((i < 8 ? i+1 : 8),pool.numIdle(""));
+        }
+    }
+
+    public void testMaxActive() {
+        pool.setMaxActive(3);
+        pool.setWhenExhaustedAction(pool.WHEN_EXHAUSTED_FAIL);
+
+        pool.borrowObject("");
+        pool.borrowObject("");
+        pool.borrowObject("");
+        try {
+            pool.borrowObject("");
+            fail("Shouldn't get here.");
+        } catch(java.util.NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    public void testEviction() {
+        pool.setMaxIdle(500);
+        pool.setMaxActive(500);
+        pool.setNumTestsPerEvictionRun(100);
+        pool.setMinEvictableIdleTimeMillis(500L);
+        pool.setTimeBetweenEvictionRunsMillis(500L);
+
+        Object[] active = new Object[500];
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject("",active[i]);
+        }
+
+        try { Thread.currentThread().sleep(2000L); } catch(Exception e) { }
+        assert("Should be less than 500 idle, found " + pool.numIdle(""),pool.numIdle("") < 500);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 400 idle, found " + pool.numIdle(""),pool.numIdle("") < 400);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 300 idle, found " + pool.numIdle(""),pool.numIdle("") < 300);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 200 idle, found " + pool.numIdle(""),pool.numIdle("") < 200);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 100 idle, found " + pool.numIdle(""),pool.numIdle("") < 100);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assertEquals("Should be zero idle, found " + pool.numIdle(""),0,pool.numIdle(""));
+
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject("",active[i]);
+        }
+
+        try { Thread.currentThread().sleep(2000L); } catch(Exception e) { }
+        assert("Should be less than 500 idle, found " + pool.numIdle(""),pool.numIdle("") < 500);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 400 idle, found " + pool.numIdle(""),pool.numIdle("") < 400);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 300 idle, found " + pool.numIdle(""),pool.numIdle("") < 300);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 200 idle, found " + pool.numIdle(""),pool.numIdle("") < 200);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 100 idle, found " + pool.numIdle(""),pool.numIdle("") < 100);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assertEquals("Should be zero idle, found " + pool.numIdle(""),0,pool.numIdle(""));
+    }
+
+    public void testEviction2() {
+        pool.setMaxIdle(500);
+        pool.setMaxActive(500);
+        pool.setNumTestsPerEvictionRun(100);
+        pool.setMinEvictableIdleTimeMillis(500L);
+        pool.setTimeBetweenEvictionRunsMillis(500L);
+
+        Object[] active = new Object[500];
+        Object[] active2 = new Object[500];
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject("");
+            active2[i] = pool.borrowObject("2");
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject("",active[i]);
+            pool.returnObject("2",active2[i]);
+        }
+
+        try { Thread.currentThread().sleep(2000L); } catch(Exception e) { }
+        assert("Should be less than 1000 idle, found " + pool.numIdle(),pool.numIdle() < 1000);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 900 idle, found " + pool.numIdle(),pool.numIdle() < 900);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 800 idle, found " + pool.numIdle(),pool.numIdle() < 800);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 700 idle, found " + pool.numIdle(),pool.numIdle() < 700);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 600 idle, found " + pool.numIdle(),pool.numIdle() < 600);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 500 idle, found " + pool.numIdle(),pool.numIdle() < 500);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 400 idle, found " + pool.numIdle(),pool.numIdle() < 400);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 300 idle, found " + pool.numIdle(),pool.numIdle() < 300);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 200 idle, found " + pool.numIdle(),pool.numIdle() < 200);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 100 idle, found " + pool.numIdle(),pool.numIdle() < 100);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assertEquals("Should be zero idle, found " + pool.numIdle(),0,pool.numIdle());
+    }
+
+    public void testThreaded1() {
+        pool.setMaxActive(15);
+        pool.setMaxIdle(15);
+        pool.setMaxWait(1000L);
+        TestThread[] threads = new TestThread[20];
+        for(int i=0;i<20;i++) {
+            threads[i] = new TestThread(pool,100,50);
+            Thread t = new Thread(threads[i]);
+            t.start();
+        }
+        for(int i=0;i<20;i++) {
+            while(!(threads[i]).complete()) {
+                try {
+                    Thread.currentThread().sleep(500L);
+                } catch(Exception e) {
+                    // ignored
+                }
+            }
+            if(threads[i].failed()) {
+                fail();
+            }
+        }
+    }
+
+    class TestThread implements Runnable {
+        java.util.Random _random = new java.util.Random();
+        KeyedObjectPool _pool = null;
+        boolean _complete = false;
+        boolean _failed = false;
+        int _iter = 100;
+        int _delay = 50;
+
+        public TestThread(KeyedObjectPool pool) {
+            _pool = pool;
+        }
+
+        public TestThread(KeyedObjectPool pool, int iter) {
+            _pool = pool;
+            _iter = iter;
+        }
+
+        public TestThread(KeyedObjectPool pool, int iter, int delay) {
+            _pool = pool;
+            _iter = iter;
+            _delay = delay;
+        }
+
+        public boolean complete() {
+            return _complete;
+        }
+
+        public boolean failed() {
+            return _failed;
+        }
+
+        public void run() {
+            for(int i=0;i<_iter;i++) {
+                try {
+                    Thread.currentThread().sleep((long)_random.nextInt(_delay));
+                } catch(Exception e) {
+                    // ignored
+                }
+                Object obj = null;
+                try {
+                    obj = _pool.borrowObject("");
+                } catch(java.util.NoSuchElementException e) {
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+
+                try {
+                    Thread.currentThread().sleep((long)_random.nextInt(_delay));
+                } catch(Exception e) {
+                    // ignored
+                }
+                _pool.returnObject("",obj);
+            }
+            _complete = true;
+        }
+    }
+}
+
+
diff --git a/src/test/org/apache/commons/pool/impl/TestGenericObjectPool.java b/src/test/org/apache/commons/pool/impl/TestGenericObjectPool.java
new file mode 100644
index 0000000..8489a06
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestGenericObjectPool.java
@@ -0,0 +1,329 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/test/org/apache/commons/pool/impl/TestGenericObjectPool.java,v 1.1 2001/04/14 16:42:13 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:42:13 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import junit.framework.*;
+import org.apache.commons.pool.*;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Id: TestGenericObjectPool.java,v 1.1 2001/04/14 16:42:13 rwaldhoff Exp $
+ */
+public class TestGenericObjectPool extends TestCase {
+    public TestGenericObjectPool(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TestGenericObjectPool.class);
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestGenericObjectPool.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+
+    private GenericObjectPool pool = null;
+
+    public void setUp() {
+        pool = new GenericObjectPool(
+            new PoolableObjectFactory()  {
+                int counter = 0;
+                public Object makeObject() { return String.valueOf(counter++); }
+                public void destroyObject(Object obj) { }
+                public boolean validateObject(Object obj) { return true; }
+                public void activateObject(Object obj) { }
+                public void passivateObject(Object obj) { }
+            }
+            );
+    }
+
+    public void testBorrow() {
+        Object obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+    }
+
+    public void testBorrowReturn() {
+        Object obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        pool.returnObject(obj2);
+        obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        pool.returnObject(obj1);
+        obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        pool.returnObject(obj0);
+        pool.returnObject(obj2);
+        obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+    }
+
+    public void testNumActiveNumIdle() {
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj0 = pool.borrowObject();
+        assertEquals(1,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj1 = pool.borrowObject();
+        assertEquals(2,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        pool.returnObject(obj1);
+        assertEquals(1,pool.numActive());
+        assertEquals(1,pool.numIdle());
+        pool.returnObject(obj0);
+        assertEquals(0,pool.numActive());
+        assertEquals(2,pool.numIdle());
+    }
+
+    public void testClear() {
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj0 = pool.borrowObject();
+        Object obj1 = pool.borrowObject();
+        assertEquals(2,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        pool.returnObject(obj1);
+        pool.returnObject(obj0);
+        assertEquals(0,pool.numActive());
+        assertEquals(2,pool.numIdle());
+        pool.clear();
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+    }
+
+    public void testMaxIdle() {
+        pool.setMaxActive(100);
+        pool.setMaxIdle(8);
+        Object[] active = new Object[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject();
+        }
+        assertEquals(100,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        for(int i=0;i<100;i++) {
+            pool.returnObject(active[i]);
+            assertEquals(99 - i,pool.numActive());
+            assertEquals((i < 8 ? i+1 : 8),pool.numIdle());
+        }
+    }
+
+    public void testMaxActive() {
+        pool.setMaxActive(3);
+        pool.setWhenExhaustedAction(pool.WHEN_EXHAUSTED_FAIL);
+
+        pool.borrowObject();
+        pool.borrowObject();
+        pool.borrowObject();
+        try {
+            pool.borrowObject();
+            fail("Shouldn't get here.");
+        } catch(java.util.NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    public void testEviction() {
+        pool.setMaxIdle(500);
+        pool.setMaxActive(500);
+        pool.setNumTestsPerEvictionRun(100);
+        pool.setMinEvictableIdleTimeMillis(500L);
+        pool.setTimeBetweenEvictionRunsMillis(500L);
+
+        Object[] active = new Object[500];
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject();
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.currentThread().sleep(2000L); } catch(Exception e) { }
+        assert("Should be less than 500 idle, found " + pool.numIdle(),pool.numIdle() < 500);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 400 idle, found " + pool.numIdle(),pool.numIdle() < 400);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 300 idle, found " + pool.numIdle(),pool.numIdle() < 300);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 200 idle, found " + pool.numIdle(),pool.numIdle() < 200);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 100 idle, found " + pool.numIdle(),pool.numIdle() < 100);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assertEquals("Should be zero idle, found " + pool.numIdle(),0,pool.numIdle());
+
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject();
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.currentThread().sleep(2000L); } catch(Exception e) { }
+        assert("Should be less than 500 idle, found " + pool.numIdle(),pool.numIdle() < 500);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 400 idle, found " + pool.numIdle(),pool.numIdle() < 400);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 300 idle, found " + pool.numIdle(),pool.numIdle() < 300);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 200 idle, found " + pool.numIdle(),pool.numIdle() < 200);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assert("Should be less than 100 idle, found " + pool.numIdle(),pool.numIdle() < 100);
+        try { Thread.currentThread().sleep(600L); } catch(Exception e) { }
+        assertEquals("Should be zero idle, found " + pool.numIdle(),0,pool.numIdle());
+    }
+
+
+    public void testThreaded1() {
+        pool.setMaxActive(15);
+        pool.setMaxIdle(15);
+        pool.setMaxWait(1000L);
+        TestThread[] threads = new TestThread[20];
+        for(int i=0;i<20;i++) {
+            threads[i] = new TestThread(pool,100,50);
+            Thread t = new Thread(threads[i]);
+            t.start();
+        }
+        for(int i=0;i<20;i++) {
+            while(!(threads[i]).complete()) {
+                try {
+                    Thread.currentThread().sleep(500L);
+                } catch(Exception e) {
+                    // ignored
+                }
+            }
+            if(threads[i].failed()) {
+                fail();
+            }
+        }
+    }
+
+    class TestThread implements Runnable {
+        java.util.Random _random = new java.util.Random();
+        ObjectPool _pool = null;
+        boolean _complete = false;
+        boolean _failed = false;
+        int _iter = 100;
+        int _delay = 50;
+
+        public TestThread(ObjectPool pool) {
+            _pool = pool;
+        }
+
+        public TestThread(ObjectPool pool, int iter) {
+            _pool = pool;
+            _iter = iter;
+        }
+
+        public TestThread(ObjectPool pool, int iter, int delay) {
+            _pool = pool;
+            _iter = iter;
+            _delay = delay;
+        }
+
+        public boolean complete() {
+            return _complete;
+        }
+
+        public boolean failed() {
+            return _failed;
+        }
+
+        public void run() {
+            for(int i=0;i<_iter;i++) {
+                try {
+                    Thread.currentThread().sleep((long)_random.nextInt(_delay));
+                } catch(Exception e) {
+                    // ignored
+                }
+                Object obj = null;
+                try {
+                    obj = _pool.borrowObject();
+                } catch(java.util.NoSuchElementException e) {
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+
+                try {
+                    Thread.currentThread().sleep((long)_random.nextInt(_delay));
+                } catch(Exception e) {
+                    // ignored
+                }
+                _pool.returnObject(obj);
+            }
+            _complete = true;
+        }
+    }
+}
+
+
diff --git a/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPool.java b/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPool.java
new file mode 100644
index 0000000..d9f5425
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPool.java
@@ -0,0 +1,227 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPool.java,v 1.1 2001/04/14 16:42:16 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:42:16 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import junit.framework.*;
+import org.apache.commons.pool.*;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Id: TestStackKeyedObjectPool.java,v 1.1 2001/04/14 16:42:16 rwaldhoff Exp $
+ */
+public class TestStackKeyedObjectPool extends TestCase {
+    public TestStackKeyedObjectPool(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TestStackKeyedObjectPool.class);
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestStackKeyedObjectPool.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+
+    private StackKeyedObjectPool pool = null;
+
+    public void setUp() {
+        pool = new StackKeyedObjectPool(
+            new KeyedPoolableObjectFactory()  {
+                int counter = 0;
+                public Object makeObject(Object key) { return String.valueOf(key) + String.valueOf(counter++); }
+                public void destroyObject(Object key, Object obj) { }
+                public boolean validateObject(Object key, Object obj) { return true; }
+                public void activateObject(Object key, Object obj) { }
+                public void passivateObject(Object key, Object obj) { }
+            }
+            );
+    }
+
+    public void testBorrow() {
+        Object obj0 = pool.borrowObject("");
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject("");
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+    }
+
+    public void testBorrowReturn() {
+        Object obj0 = pool.borrowObject("");
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject("");
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+        pool.returnObject("",obj2);
+        obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+        pool.returnObject("",obj1);
+        obj1 = pool.borrowObject("");
+        assertEquals("1",obj1);
+        pool.returnObject("",obj0);
+        pool.returnObject("",obj2);
+        obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+        obj0 = pool.borrowObject("");
+        assertEquals("0",obj0);
+    }
+
+    public void testNumActiveNumIdle() {
+        assertEquals(0,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj0 = pool.borrowObject("");
+        assertEquals(1,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj1 = pool.borrowObject("");
+        assertEquals(2,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        pool.returnObject("",obj1);
+        assertEquals(1,pool.numActive(""));
+        assertEquals(1,pool.numIdle(""));
+        pool.returnObject("",obj0);
+        assertEquals(0,pool.numActive(""));
+        assertEquals(2,pool.numIdle(""));
+    }
+
+    public void testNumActiveNumIdle2() {
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        assertEquals(0,pool.numActive("A"));
+        assertEquals(0,pool.numIdle("A"));
+        assertEquals(0,pool.numActive("B"));
+        assertEquals(0,pool.numIdle("B"));
+
+        Object objA0 = pool.borrowObject("A");
+        Object objB0 = pool.borrowObject("B");
+
+        assertEquals(2,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        assertEquals(1,pool.numActive("A"));
+        assertEquals(0,pool.numIdle("A"));
+        assertEquals(1,pool.numActive("B"));
+        assertEquals(0,pool.numIdle("B"));
+
+        Object objA1 = pool.borrowObject("A");
+        Object objB1 = pool.borrowObject("B");
+
+        assertEquals(4,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        assertEquals(2,pool.numActive("A"));
+        assertEquals(0,pool.numIdle("A"));
+        assertEquals(2,pool.numActive("B"));
+        assertEquals(0,pool.numIdle("B"));
+
+        pool.returnObject("A",objA0);
+        pool.returnObject("B",objB0);
+
+        assertEquals(2,pool.numActive());
+        assertEquals(2,pool.numIdle());
+        assertEquals(1,pool.numActive("A"));
+        assertEquals(1,pool.numIdle("A"));
+        assertEquals(1,pool.numActive("B"));
+        assertEquals(1,pool.numIdle("B"));
+
+        pool.returnObject("A",objA1);
+        pool.returnObject("B",objB1);
+
+        assertEquals(0,pool.numActive());
+        assertEquals(4,pool.numIdle());
+        assertEquals(0,pool.numActive("A"));
+        assertEquals(2,pool.numIdle("A"));
+        assertEquals(0,pool.numActive("B"));
+        assertEquals(2,pool.numIdle("B"));
+    }
+
+    public void testClear() {
+        assertEquals(0,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj0 = pool.borrowObject("");
+        Object obj1 = pool.borrowObject("");
+        assertEquals(2,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        pool.returnObject("",obj1);
+        pool.returnObject("",obj0);
+        assertEquals(0,pool.numActive(""));
+        assertEquals(2,pool.numIdle(""));
+        pool.clear("");
+        assertEquals(0,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        Object obj2 = pool.borrowObject("");
+        assertEquals("2",obj2);
+    }
+
+    public void testIdleCap() {
+        Object[] active = new Object[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        assertEquals(100,pool.numActive(""));
+        assertEquals(0,pool.numIdle(""));
+        for(int i=0;i<100;i++) {
+            pool.returnObject("",active[i]);
+            assertEquals(99 - i,pool.numActive(""));
+            assertEquals((i < 8 ? i+1 : 8),pool.numIdle(""));
+        }
+    }
+}
diff --git a/src/test/org/apache/commons/pool/impl/TestStackObjectPool.java b/src/test/org/apache/commons/pool/impl/TestStackObjectPool.java
new file mode 100644
index 0000000..40f828c
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestStackObjectPool.java
@@ -0,0 +1,178 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//pool/src/test/org/apache/commons/pool/impl/TestStackObjectPool.java,v 1.1 2001/04/14 16:42:18 rwaldhoff Exp $
+ * $Revision: 1.1 $
+ * $Date: 2001/04/14 16:42:18 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.pool.impl;
+
+import junit.framework.*;
+import org.apache.commons.pool.*;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Id: TestStackObjectPool.java,v 1.1 2001/04/14 16:42:18 rwaldhoff Exp $
+ */
+public class TestStackObjectPool extends TestCase {
+    public TestStackObjectPool(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TestStackObjectPool.class);
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestStackObjectPool.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+
+    private StackObjectPool pool = null;
+
+    public void setUp() {
+        pool = new StackObjectPool(
+            new PoolableObjectFactory()  {
+                int counter = 0;
+                public Object makeObject() { return String.valueOf(counter++); }
+                public void destroyObject(Object obj) { }
+                public boolean validateObject(Object obj) { return true; }
+                public void activateObject(Object obj) { }
+                public void passivateObject(Object obj) { }
+            }
+            );
+    }
+
+    public void testBorrow() {
+        Object obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+    }
+
+    public void testBorrowReturn() {
+        Object obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+        Object obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        pool.returnObject(obj2);
+        obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        pool.returnObject(obj1);
+        obj1 = pool.borrowObject();
+        assertEquals("1",obj1);
+        pool.returnObject(obj0);
+        pool.returnObject(obj2);
+        obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+        obj0 = pool.borrowObject();
+        assertEquals("0",obj0);
+    }
+
+    public void testNumActiveNumIdle() {
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj0 = pool.borrowObject();
+        assertEquals(1,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj1 = pool.borrowObject();
+        assertEquals(2,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        pool.returnObject(obj1);
+        assertEquals(1,pool.numActive());
+        assertEquals(1,pool.numIdle());
+        pool.returnObject(obj0);
+        assertEquals(0,pool.numActive());
+        assertEquals(2,pool.numIdle());
+    }
+
+    public void testClear() {
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj0 = pool.borrowObject();
+        Object obj1 = pool.borrowObject();
+        assertEquals(2,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        pool.returnObject(obj1);
+        pool.returnObject(obj0);
+        assertEquals(0,pool.numActive());
+        assertEquals(2,pool.numIdle());
+        pool.clear();
+        assertEquals(0,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        Object obj2 = pool.borrowObject();
+        assertEquals("2",obj2);
+    }
+
+    public void testIdleCap() {
+        Object[] active = new Object[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject();
+        }
+        assertEquals(100,pool.numActive());
+        assertEquals(0,pool.numIdle());
+        for(int i=0;i<100;i++) {
+            pool.returnObject(active[i]);
+            assertEquals(99 - i,pool.numActive());
+            assertEquals((i < 8 ? i+1 : 8),pool.numIdle());
+        }
+    }
+}