[commons-pool] 01/06: Imported Upstream version 1.6

Tony Mancill tmancill at moszumanska.debian.org
Thu Jul 7 05:02:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

tmancill pushed a commit to branch master
in repository commons-pool.

commit be2f57e39e787b5208db1dc5a42a0d1809c212ee
Author: tony mancill <tmancill at debian.org>
Date:   Wed Jul 6 21:28:01 2016 -0700

    Imported Upstream version 1.6
---
 LICENSE.txt                                        |  202 ++
 NOTICE.txt                                         |    5 +
 README.txt                                         |    2 +
 RELEASE-NOTES.txt                                  |   16 +
 build.xml                                          |  184 ++
 checkstyle.xml                                     |   92 +
 findbugs-exclude-filter.xml                        |   58 +
 license-header.txt                                 |   16 +
 pom.xml                                            |  295 +++
 src/assembly/bin.xml                               |   44 +
 src/assembly/src.xml                               |   42 +
 src/changes/changes.xml                            |  311 +++
 .../apache/commons/pool/BaseKeyedObjectPool.java   |  166 ++
 .../pool/BaseKeyedPoolableObjectFactory.java       |   97 +
 .../org/apache/commons/pool/BaseObjectPool.java    |  146 ++
 .../commons/pool/BasePoolableObjectFactory.java    |   76 +
 .../org/apache/commons/pool/KeyedObjectPool.java   |  233 ++
 .../commons/pool/KeyedObjectPoolFactory.java       |   39 +
 .../commons/pool/KeyedPoolableObjectFactory.java   |  142 +
 src/java/org/apache/commons/pool/ObjectPool.java   |  193 ++
 .../org/apache/commons/pool/ObjectPoolFactory.java |   38 +
 src/java/org/apache/commons/pool/PoolUtils.java    | 2476 ++++++++++++++++++
 .../apache/commons/pool/PoolableObjectFactory.java |  140 +
 .../commons/pool/impl/CursorableLinkedList.java    | 1516 +++++++++++
 .../apache/commons/pool/impl/EvictionTimer.java    |  130 +
 .../commons/pool/impl/GenericKeyedObjectPool.java  | 2749 ++++++++++++++++++++
 .../pool/impl/GenericKeyedObjectPoolFactory.java   |  473 ++++
 .../commons/pool/impl/GenericObjectPool.java       | 2093 +++++++++++++++
 .../pool/impl/GenericObjectPoolFactory.java        |  459 ++++
 .../commons/pool/impl/SoftReferenceObjectPool.java |  379 +++
 .../commons/pool/impl/StackKeyedObjectPool.java    |  654 +++++
 .../pool/impl/StackKeyedObjectPoolFactory.java     |  165 ++
 .../apache/commons/pool/impl/StackObjectPool.java  |  471 ++++
 .../commons/pool/impl/StackObjectPoolFactory.java  |  168 ++
 src/java/org/apache/commons/pool/impl/package.html |   56 +
 src/java/org/apache/commons/pool/overview.html     |  121 +
 src/java/org/apache/commons/pool/package.html      |   69 +
 src/site/resources/images/pool-logo-blue.jpg       |  Bin 0 -> 4512 bytes
 src/site/resources/images/pool-logo-white.png      |  Bin 0 -> 11730 bytes
 src/site/resources/images/pool-logo-white.xcf      |  Bin 0 -> 21196 bytes
 .../images/uml/GenericKeyedObjectPool.gif          |  Bin 0 -> 44928 bytes
 .../resources/images/uml/GenericObjectPool.gif     |  Bin 0 -> 41664 bytes
 src/site/resources/images/uml/KeyedObjectPool.gif  |  Bin 0 -> 3339 bytes
 src/site/resources/images/uml/ObjectPool.gif       |  Bin 0 -> 3478 bytes
 src/site/resources/images/uml/borrowObject.gif     |  Bin 0 -> 21743 bytes
 src/site/resources/images/uml/invalidateObject.gif |  Bin 0 -> 14245 bytes
 src/site/resources/images/uml/returnObject.gif     |  Bin 0 -> 18684 bytes
 src/site/site.xml                                  |   50 +
 src/site/xdoc/download_pool.xml                    |  138 +
 src/site/xdoc/downloads.xml                        |   60 +
 src/site/xdoc/examples.xml                         |  175 ++
 src/site/xdoc/guide/classdiagrams.xml              |   38 +
 src/site/xdoc/guide/index.xml                      |   77 +
 src/site/xdoc/guide/sequencediagrams.xml           |   41 +
 src/site/xdoc/index.xml                            |  172 ++
 src/site/xdoc/issue-tracking.xml                   |  102 +
 src/site/xdoc/mail-lists.xml                       |  202 ++
 src/template/pool-release-notes.vm                 |  119 +
 src/test/org/apache/commons/pool/MethodCall.java   |  117 +
 .../pool/MethodCallPoolableObjectFactory.java      |  154 ++
 .../org/apache/commons/pool/PrivateException.java  |   27 +
 .../commons/pool/TestBaseKeyedObjectPool.java      |  367 +++
 .../pool/TestBaseKeyedPoolableObjectFactory.java   |   43 +
 .../apache/commons/pool/TestBaseObjectPool.java    |  283 ++
 .../pool/TestBasePoolableObjectFactory.java        |   43 +
 .../apache/commons/pool/TestKeyedObjectPool.java   |  521 ++++
 .../commons/pool/TestKeyedObjectPoolFactory.java   |   69 +
 .../org/apache/commons/pool/TestObjectPool.java    |  439 ++++
 .../apache/commons/pool/TestObjectPoolFactory.java |   65 +
 .../org/apache/commons/pool/TestPoolUtils.java     |  999 +++++++
 src/test/org/apache/commons/pool/VisitTracker.java |  104 +
 .../apache/commons/pool/VisitTrackerFactory.java   |   64 +
 src/test/org/apache/commons/pool/Waiter.java       |  132 +
 .../org/apache/commons/pool/WaiterFactory.java     |  208 ++
 .../pool/impl/TestGenericKeyedObjectPool.java      | 1756 +++++++++++++
 .../impl/TestGenericKeyedObjectPoolFactory.java    |  160 ++
 .../commons/pool/impl/TestGenericObjectPool.java   | 1827 +++++++++++++
 .../pool/impl/TestGenericObjectPoolFactory.java    |  190 ++
 .../commons/pool/impl/TestSoftRefOutOfMemory.java  |  270 ++
 .../pool/impl/TestSoftReferenceObjectPool.java     |   68 +
 .../pool/impl/TestStackKeyedObjectPool.java        |  328 +++
 .../pool/impl/TestStackKeyedObjectPoolFactory.java |   71 +
 .../commons/pool/impl/TestStackObjectPool.java     |  634 +++++
 .../pool/impl/TestStackObjectPoolFactory.java      |   65 +
 .../commons/pool/performance/PerformanceTest.java  |  183 ++
 .../pool/performance/SleepingObjectFactory.java    |   83 +
 86 files changed, 24960 insertions(+)

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..75b5248
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 0000000..2f8f55b
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Commons Pool
+Copyright 2001-2012 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..1188bfe
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,2 @@
+See http://commons.apache.org/pool/ for additional and 
+up-to-date information on Commons Pool.
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
new file mode 100644
index 0000000..e9cb2c2
--- /dev/null
+++ b/RELEASE-NOTES.txt
@@ -0,0 +1,16 @@
+commons-pool-1.6 RELEASE NOTES
+
+This release adds support for Java 5 generics and therefore requires Java 5 for source compatibility.
+This release is binary compatible with 1.5.7.
+
+The only change in this version is:
+
+o Support Java 1.5 Generics in version 1.x.  Issue: POOL-208. 
+
+ 
+For complete information on commons-pool, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the commons-pool website:
+
+http://commons.apache.org/pool/
+
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..1ed9785
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,184 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<!-- $Id: build.xml 1226157 2011-12-31 18:21:39Z ggregory $ -->
+<project name="commons-pool" default="test" basedir=".">
+
+   <target name="init">
+      <tstamp/>
+
+      <!-- read properties from the build.properties, if any -->
+      <property name="component-propfile" value="${basedir}/build.properties"/>
+      <property file="${component-propfile}"/>
+
+      <!-- read properties from the commons build.properties, if any -->
+      <property name="commons-propfile" value="${basedir}/../build.properties"/>
+      <property file="${commons-propfile}"/>
+
+      <!-- read properties from the ${user.home}/propfile, if any -->
+      <property name="user-propfile" value="${user.home}/build.properties"/>
+      <property file="${user-propfile}"/>
+
+      <!-- command line classpath, if any -->
+      <property name="cp" value=""/>
+
+      <!-- now combine the classpaths -->
+      <property name="classpath" value="${cp}"/>
+
+      <property name="name" value="commons-pool"/>
+      <property name="title" value="Apache Commons Object Pooling Package"/>
+      <property name="version" value="1.6-SNAPSHOT"/>
+      <property name="package" value="org.apache.commons.pool.*"/>
+
+      <property name="src.dir" value="${basedir}/src"/>
+      <property name="src.java.dir" value="${src.dir}/java"/>
+      <property name="src.test.dir" value="${src.dir}/test"/>
+      <property name="build.dir" value="${basedir}/build"/>
+      <property name="build.classes.dir" value="${build.dir}/classes"/>
+      <property name="build.test-classes.dir" value="${build.dir}/test-classes"/>
+      <property name="dist.dir" value="${basedir}/dist"/>
+      <property name="dist.jar" value="${dist.dir}/${name}-${version}.jar"/>
+
+      <property name="test.failonerror" value="true" /> 
+     
+      <property name="javadoc.dir" value="${dist.dir}/docs/api"/>
+      <property name="javadoc.bottom" value="<small>Copyright &copy; 2001-2012 Apache Software Foundation. Documenation generated ${TODAY}</small>."/>
+      <property name="javadoc.overview" value="${src.java.dir}/org/apache/commons/pool/overview.html" />
+
+      <property name="javac.optimize" value="false"/>
+      <property name="javac.debug" value="true"/>
+      <property name="javac.deprecation" value="true"/>
+      <property name="javac.target.version" value="1.5"/>
+      <property name="javac.src.version" value="1.5"/>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="clean" depends="init" description="removes generated files">
+      <delete dir="${build.dir}"/>
+      <delete dir="${dist.dir}"/>
+   </target>
+
+   <target name="javadoc" depends="init" description="generates javadocs">
+      <mkdir dir="${javadoc.dir}"/>
+      <javadoc packagenames="org.*"
+               sourcepath="${src.java.dir}"
+               classpath="${classpath}"
+               destdir="${javadoc.dir}"
+               windowtitle="${title}"
+               doctitle="${title}"
+               bottom="${javadoc.bottom}"
+               overview="${javadoc.overview}"
+               public="true"
+               version="true"
+               author="true"
+               splitindex="false"
+               nodeprecated="true"
+               nodeprecatedlist="true"
+               notree="true"
+               noindex="false"
+               nohelp="true"
+               nonavbar="false"
+               serialwarn="false"
+               source="${javac.src.version}">
+          <link href="http://java.sun.com/j2se/1.4.2/docs/api"/>
+      </javadoc>
+   </target>
+
+   <!-- ######################################################### -->
+
+   <target name="compile" depends="init" description="compiles source files">
+      <mkdir dir="${build.classes.dir}"/>
+      <javac destdir="${build.classes.dir}"
+             srcdir="${src.java.dir}"
+             classpath="${classpath}"
+             debug="${javac.debug}"
+             deprecation="${javac.deprecation}"
+             optimize="${javac.optimize}"
+             target="${javac.target.version}"
+             source="${javac.src.version}"/>
+   </target>
+   
+   <target name="compile-test" depends="compile">
+      <mkdir dir="${build.test-classes.dir}"/>
+      <javac destdir="${build.test-classes.dir}"
+             srcdir="${src.test.dir}"
+             debug="${javac.debug}"
+             deprecation="${javac.deprecation}"
+             optimize="${javac.optimize}"
+             target="${javac.target.version}"
+             source="${javac.src.version}">
+        <classpath>
+          <pathelement location="${build.classes.dir}" /> 
+          <pathelement location="${build.test-classes.dir}" /> 
+          <pathelement path="${classpath}" /> 
+        </classpath>
+      </javac>
+   </target>
+
+   <target name="test" depends="compile-test" description="runs (junit) unit tests">
+      <echo message="Because we need to sleep to test the eviction threads, this takes a little while (around 2 minutes)..."/>
+      <junit printsummary="true" showoutput="true" fork="yes" haltonerror="${test.failonerror}">
+        <classpath>
+          <pathelement location="${build.classes.dir}" /> 
+          <pathelement location="${build.test-classes.dir}" /> 
+          <pathelement path="${classpath}" /> 
+        </classpath>
+        <!-- If test.entry is defined, run a single test, otherwise run all valid tests -->
+        <test name="${test.entry}" if="test.entry"/>
+        <batchtest todir="${build.dir}/ant-reports" unless="test.entry">
+          <fileset dir="${src.test.dir}">
+            <include name="**/Test*.java"/>
+            <exclude name="org/apache/commons/pool/impl/TestSoftRefOutOfMemory.java"/>
+            <!-- abstract classes -->
+            <exclude name="**/TestObjectPool.java"/>
+            <exclude name="**/TestObjectPoolFactory.java"/>
+            <exclude name="**/TestKeyedObjectPool.java"/>
+            <exclude name="**/TestKeyedObjectPoolFactory.java"/>
+          </fileset>
+        </batchtest>
+      </junit>
+   </target>
+
+   <target name="build-jar" depends="compile">
+      <mkdir dir="${dist.dir}"/>
+      <jar jarfile="${dist.jar}">
+         <fileset dir="${build.classes.dir}"/>
+         <metainf dir="${basedir}" includes="LICENSE.txt,NOTICE.txt"/>
+         <manifest>
+            <attribute name="Built-By" value="${user.name}"/>
+            <attribute name="Package" value="${package}"/>
+            <attribute name="Extension-Name" value="${name}"/>
+            <attribute name="Specification-Vendor" value="Apache Software Foundation"/>
+            <attribute name="Specification-Title" value="${title}"/>
+            <attribute name="Implementation-Version" value="${version}"/>
+            <attribute name="Implementation-Vendor" value="Apache Software Foundation"/>
+            <attribute name="X-Compile-Source-JDK" value="${javac.src.version}"/>
+            <attribute name="X-Compile-Target-JDK" value="${javac.target.version}"/>
+
+         </manifest>
+      </jar>
+   </target>
+
+   <target name="build" depends="clean,build-jar,javadoc">
+      <copy todir="${dist.dir}" file="${basedir}/LICENSE.txt"/>
+      <copy todir="${dist.dir}" file="${basedir}/NOTICE.txt"/>
+      <copy todir="${dist.dir}" file="${basedir}/README.txt"/>
+   </target>
+
+   <target name="dist" depends="build" description="gump target"/>
+   
+</project>
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..68e8fa5
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<!--
+  Checkstyle checks configured for Maven.
+-->
+
+<module name="Checker">
+
+  <property name="localeLanguage" value="en"/>
+
+  <module name="TreeWalker">
+   
+    <!-- Verify that EVERY source file has the appropriate license -->
+    <module name="Header">
+      <property name="headerFile" value="${checkstyle.header.file}"/>
+    </module>
+
+    <!-- Operator must be at end of wrapped line -->
+    <module name="OperatorWrap">
+      <property name="option" value="eol"/>
+    </module>
+    
+    <!-- Interfaces must be types (not just constants) -->
+    <module name="InterfaceIsType"/>
+
+    <!-- Must have class / interface header comments -->
+    <module name="JavadocType"/>
+        
+     <!-- Require method javadocs, allow undeclared RTE -->
+    <module name="JavadocMethod">
+      <property name="allowUndeclaredRTE" value="true"/>
+      <property name="allowMissingThrowsTags" value="true"/>
+    </module>
+        
+    <!-- Require field javadoc -->
+    <module name="JavadocVariable"/>
+        
+    <!-- No tabs allowed! -->
+    <module name="TabCharacter"/>
+    
+    <!-- No public fields -->
+    <module name="VisibilityModifier">
+       <property name="protectedAllowed" value="true"/>
+    </module>
+    
+    <!-- Require hash code override when equals is -->
+    <module name="EqualsHashCode"/>
+    
+    <!-- Disallow unnecessary instantiation of Boolean, String -->
+    <module name="IllegalInstantiation">
+      <property name="classes" value="java.lang.Boolean, java.lang.String"/>
+    </module>
+
+    <!-- Required for SuppressionCommentFilter below -->
+    <module name="FileContentsHolder"/>
+
+  </module>
+  
+  <!-- Require package javadoc -->
+  <module name="PackageHtml"/>
+
+  <!-- Setup special comments to suppress specific checks from source files -->
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="CHECKSTYLE\: stop all checks"/>
+    <property name="onCommentFormat"  value="CHECKSTYLE\: resume checking"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="CHECKSTYLE\: stop VisibilityModifier"/>
+    <property name="onCommentFormat"  value="CHECKSTYLE\: resume VisibilityModifier"/>
+    <property name="checkFormat"      value="VisibilityModifier"/>
+  </module>
+
+</module>
diff --git a/findbugs-exclude-filter.xml b/findbugs-exclude-filter.xml
new file mode 100644
index 0000000..173f954
--- /dev/null
+++ b/findbugs-exclude-filter.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<!--
+  This file contains some false positive bugs detected by findbugs. Their
+  false positive nature has been analyzed individually and they have been
+  put here to instruct findbugs to ignore them.
+-->
+<FindBugsFilter>
+  <!-- findbugs thinks these is naked, but they are not -->
+  <Match>
+    <Class name="org.apache.commons.pool.impl.GenericKeyedObjectPool" />
+    <Method name="allocate"/>
+    <Bug pattern="NN_NAKED_NOTIFY" />
+  </Match>
+  <Match>
+    <Class name="org.apache.commons.pool.impl.GenericKeyedObjectPool" />
+    <Method name="close"/>
+    <Bug pattern="NN_NAKED_NOTIFY" />
+  </Match>
+  <Match>
+    <Class name="org.apache.commons.pool.impl.GenericObjectPool" />
+    <Method name="close"/>
+    <Bug pattern="NN_NAKED_NOTIFY" />
+  </Match>
+  <!-- Iteration has to be over keys here -->
+  <Match>
+    <Class name="org.apache.commons.pool.impl.GenericKeyedObjectPool" />
+    <Method name="destroy"/>
+    <Bug pattern="WMI_WRONG_MAP_ITERATOR" />
+  </Match>
+  <!-- compareTo is implemented consistently with object equals in this case -->
+  <Match>
+    <Class name="org.apache.commons.pool.impl.GenericKeyedObjectPool$ObjectTimestampPair" />
+    <Bug pattern="EQ_COMPARETO_USE_OBJECT_EQUALS" />
+  </Match>
+  <!-- findbugs thinks this is naked, but it is not -->
+  <Match>
+    <Class name="org.apache.commons.pool.impl.GenericObjectPool" />
+    <Method name="allocate"/>
+    <Bug pattern="NN_NAKED_NOTIFY" />
+  </Match>
+</FindBugsFilter>
diff --git a/license-header.txt b/license-header.txt
new file mode 100644
index 0000000..b04c13b
--- /dev/null
+++ b/license-header.txt
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..07e4d7e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,295 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project
+    xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-parent</artifactId>
+    <version>22</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>commons-pool</groupId>
+  <artifactId>commons-pool</artifactId>
+  <version>1.6</version>
+  <name>Commons Pool</name>
+
+  <inceptionYear>2001</inceptionYear>
+  <description>Commons Object Pooling Library</description>
+
+  <url>http://commons.apache.org/pool/</url>
+
+  <issueManagement>
+    <system>jira</system>
+    <url>http://issues.apache.org/jira/browse/POOL</url>
+  </issueManagement>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/pool/trunk</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/pool/trunk</developerConnection>
+    <url>http://svn.apache.org/viewvc/commons/proper/pool/trunk</url>
+  </scm>
+
+  <developers>
+    <developer>
+      <name>Morgan Delagrange</name>
+      <id>morgand</id>
+      <email></email>
+      <organization></organization>
+    </developer>
+    <developer>
+      <name>Geir Magnusson</name>
+      <id>geirm</id>
+      <email></email>
+      <organization></organization>
+    </developer>
+    <developer>
+      <name>Craig McClanahan</name>
+      <id>craigmcc</id>
+      <email></email>
+      <organization></organization>
+    </developer>
+    <developer>
+      <name>Rodney Waldhoff</name>
+      <id>rwaldhoff</id>
+      <email></email>
+      <organization></organization>
+    </developer>
+    <developer>
+      <name>David Weinrich</name>
+      <id>dweinr1</id>
+      <email></email>
+      <organization></organization>
+    </developer>
+    <developer>
+      <name>Dirk Verbeeck</name>
+      <id>dirkv</id>
+      <email></email>
+      <organization></organization>
+    </developer>
+    <developer>
+      <name>Robert Burrell Donkin</name>
+      <id>rdonkin</id>
+      <email></email>
+      <organization>Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <name>Sandy McArthur</name>
+      <id>sandymac</id>
+      <email></email>
+      <organization>Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <name>Gary Gregory</name>
+      <id>ggregory</id>
+      <email></email>
+      <organization>Apache Software Foundation</organization>
+    </developer>
+  </developers>
+  <contributors>
+    <contributor>
+      <name>Todd Carmichael</name>
+      <email>toddc at concur.com</email>
+    </contributor>
+  </contributors>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <maven.compile.source>1.5</maven.compile.source>
+    <maven.compile.target>1.5</maven.compile.target>
+    <commons.componentid>pool</commons.componentid>
+    <commons.release.version>1.6</commons.release.version>
+    <commons.jira.id>POOL</commons.jira.id>
+    <commons.jira.pid>12310488</commons.jira.pid>
+  </properties>
+
+  <distributionManagement>
+    <!-- Cannot define in parent ATM, see COMMONSSITE-26 -->
+    <site>
+      <id>apache.website</id>
+      <name>Apache Commons Site</name>
+      <url>${commons.deployment.protocol}://people.apache.org/www/commons.apache.org/${commons.componentid}</url>
+    </site>
+  </distributionManagement>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-site-plugin</artifactId>
+          <version>3.0</version>
+          <dependencies>
+            <dependency>
+              <!-- add support for ssh/scp -->
+              <groupId>org.apache.maven.wagon</groupId>
+              <artifactId>wagon-ssh</artifactId>
+              <version>2.2</version>
+            </dependency>
+          </dependencies>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  
+    <sourceDirectory>src/java</sourceDirectory>
+    <testSourceDirectory>src/test</testSourceDirectory>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+            <configuration>
+              <includes>
+                <include>**/Test*.java</include>
+              </includes>
+              <excludes>
+                <!-- nested classes are not handled properly by Surefire -->
+                <exclude>**/Test*$*.java</exclude>
+                <exclude>org/apache/commons/pool/impl/TestSoftRefOutOfMemory.java</exclude>
+              </excludes>
+            </configuration>
+          </plugin>
+        <plugin>
+          <artifactId>maven-assembly-plugin</artifactId>
+          <configuration>
+            <descriptors>
+              <descriptor>src/assembly/bin.xml</descriptor>
+              <descriptor>src/assembly/src.xml</descriptor>
+            </descriptors>
+            <tarLongFileMode>gnu</tarLongFileMode>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-source-plugin</artifactId>
+          <configuration>
+            <archive>
+              <manifest>
+                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+              </manifest>
+              <manifestEntries>
+                <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+              </manifestEntries>
+            </archive>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <configuration>
+            <archive>
+              <manifest>
+                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+              </manifest>
+              <manifestEntries>
+                <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+              </manifestEntries>
+            </archive>
+          </configuration>
+        </plugin>
+      </plugins>
+    </build>
+
+    <reporting>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-changes-plugin</artifactId>
+          <version>2.6</version>
+          <configuration>
+            <xmlPath>${basedir}/src/changes/changes.xml</xmlPath>
+            <issueLinkTemplate>%URL%/%ISSUE%</issueLinkTemplate>
+            <template>pool-release-notes.vm</template>
+            <templateDirectory>src/template</templateDirectory>
+          </configuration>
+          <reportSets>
+            <reportSet>
+              <reports>
+                 <report>changes-report</report>
+              </reports>
+            </reportSet>
+          </reportSets>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>clirr-maven-plugin</artifactId>
+          <version>2.3</version>
+          <configuration>
+            <comparisonVersion>1.5.7</comparisonVersion>
+            <minSeverity>info</minSeverity>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-checkstyle-plugin</artifactId>
+          <version>2.1</version>
+          <configuration>
+            <configLocation>${basedir}/checkstyle.xml</configLocation>
+            <enableRulesSummary>false</enableRulesSummary>
+            <headerFile>${basedir}/license-header.txt</headerFile>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>findbugs-maven-plugin</artifactId>
+          <version>2.3.3</version>
+          <configuration>
+            <threshold>Normal</threshold>
+            <effort>Default</effort>
+            <excludeFilterFile>${basedir}/findbugs-exclude-filter.xml</excludeFilterFile>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>cobertura-maven-plugin</artifactId>
+          <version>2.5.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-pmd-plugin</artifactId>
+          <version>2.5</version>
+          <configuration>
+            <targetJdk>1.5</targetJdk>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>taglist-maven-plugin</artifactId>
+          <version>2.4</version>
+          <configuration>
+            <tags>
+              <tag>TODO</tag>
+              <tag>NOPMD</tag>
+              <tag>NOTE</tag>
+            </tags>
+          </configuration>
+        </plugin>
+      </plugins>
+    </reporting>
+</project>
diff --git a/src/assembly/bin.xml b/src/assembly/bin.xml
new file mode 100644
index 0000000..4c6a171
--- /dev/null
+++ b/src/assembly/bin.xml
@@ -0,0 +1,44 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+    <id>bin</id>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+    <includeSiteDirectory>false</includeSiteDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>LICENSE.txt</include>
+                <include>NOTICE.txt</include>
+                <include>RELEASE-NOTES.txt</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target/site/apidocs</directory>
+            <outputDirectory>apidocs</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/src/assembly/src.xml b/src/assembly/src.xml
new file mode 100644
index 0000000..cf18b9a
--- /dev/null
+++ b/src/assembly/src.xml
@@ -0,0 +1,42 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<assembly>
+    <id>src</id>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+    <baseDirectory>${project.artifactId}-${project.version}-src</baseDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>build.xml</include>
+                <include>checkstyle.xml</include>
+                <include>LICENSE.txt</include>
+                <include>license-header.txt</include>
+                <include>NOTICE.txt</include>
+                <include>pom.xml</include>
+                <include>README.txt</include>
+                <include>RELEASE-NOTES.txt</include>
+                <include>findbugs-exclude-filter.xml</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>src</directory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
new file mode 100644
index 0000000..709e80a
--- /dev/null
+++ b/src/changes/changes.xml
@@ -0,0 +1,311 @@
+<?xml version="1.0"?>
+<!--
+     Licensed to the Apache Software Foundation (ASF) under one or more
+     contributor license agreements.  See the NOTICE file distributed with
+     this work for additional information regarding copyright ownership.
+     The ASF licenses this file to You under the Apache License, Version 2.0
+     (the "License"); you may not use this file except in compliance with
+     the License.  You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+  -->
+<document>
+  <properties>
+    <title>Commons Pool Changes</title>
+  </properties>
+  <body>
+  <release version="1.6" date="2012-01-07" description="Adds generics and requires Java 5.">
+    <action dev="ggregory" type="add" issue="POOL-208">
+      Support Java 1.5 Generics in version 1.x.
+    </action>
+  </release>
+  <release version="1.5.7" date="2011-12-20" description="This is a patch release, including bugfixes only.">
+    <action dev="psteitz" type="fix" issue="POOL-189" due-to="Bill Speirs">
+      Awaken threads waiting on borrowObject when a pool has been closed and have them throw
+      IllegalStateException.  Prior to the fix for this issue, threads waiting in borrowObject when
+      close was invoked on GOP or GKOP would block indefinitely.
+    </action>
+    <action dev="psteitz" type="fix" issue="POOL-192" due-to="Helge Dannenberg">
+      Corrected total internal processing counter update in destroy.  Prior to the fix
+      for this issue, clear(key) was leaking capacity associated with elements in the
+      pool being cleared.
+    </action>
+  </release>
+  <release version="1.5.6" date="2011-04-03" description="This is a patch release, including bugfixes only.">
+    <action dev="markt" type="fix" issue="POOL-179" due-to="Axel Grossmann">
+      Correctly handle an InterruptedException when waiting for an object from
+      the pool.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-180">
+      Only stop tracking objects for a key when there are no idle objects, no
+      active objects and no objects being processed.
+    </action>
+    <action dev="markt" type="update" issue="POOL-181">
+      Make BaseObjectPool.isClosed() public.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-184" dut-to="Adrian Nistor">
+      Correct bug that could lead to inappropriate pool starvation when evict()
+      and borrowObject() are called concurrently.
+    </action>
+    <action dev="markt" type="fix" due-to="psteitz">
+      Fix performance issues when object destruction has latency.
+    </action>
+  </release>
+  <release version="1.5.5" date="2010-09-10" description=
+     "This is a patch release, including bugfixes, documentation improvements and some deprecations
+      in preparation for pool 2.0.">
+    <action dev="psteitz" type="update" issue="POOL-169">
+      In preparation for pool 2.0, deprecated direct access to protected fields
+      and setFactory methods.  In pool 2.0, pool object factories will be immutable.
+    </action>
+    <action dev="psteitz" type="fix" issue="POOL-158">
+      Made GenericKeyedObjectPool._minIdle volatile.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-161">
+      Made the default context class loader for the eviction thread the same loader that loads
+      the library to prevent memory leaks in multiple class loader environments.
+    </action>
+    <action dev="sebb" type="update" issue="POOL-166">
+      GenericKeyedObjectPool.destroy could use entrySet() rather than keySet() followed by get()
+    </action>
+    <action dev="psteitz" type="fix" issue="POOL-157" due-to="David Hu">
+      GenericObjectPool and GenericKeyedObjectPool setFactory methods destroy idle instances
+      in the pool by contract. Prior to the fix for this issue, newly set factories were being
+      used to destroy idle instances, rather than the factories used to create them.  The 
+      setFactory methods have also been deprecated, to be removed in version 2.0.
+    </action>
+    <action dev="sebb" type="update" issue="POOL-156">
+      ObjectPool classes can ignore Throwable. Added consistent handling for Throwables
+      that are normally swallowed including always re-throwing certain Throwables (e.g. ThreadDeath).
+    </action>
+    <action dev="markt" type="fix" issue="POOL-162">
+      When waiting threads are interrupted, GOP, GKOP may leak capacity.
+    </action>
+    <action dev="psteitz" type="fix" issue="POOL-154" due-to="Glen Mazza">
+      Documentation for the close method in GenericObjectPool and GenericKeyedObjectPool
+      incorrectly states that this method does not clear the pool.
+    </action>
+  </release>
+  <release version="1.5.4" date="2009-11-20" description=
+ "This is a patch release containing a fix for POOL-152, a regression
+introduced in version 1.5.">
+    <action dev="markt" type="fix" issue="POOL-152" due-to="Bushov Alexander">
+      GenericObjectPool can block forever in borrowObject when the pool is exhausted and a newly created
+      object fails validation. When borrowing an object if a new object is created but validate fails,
+      the latch should not be returned to the queue as an exception will be thrown.
+    </action>
+  </release>
+  <release version="1.5.3" date="2009-09-21" description=
+"This is a patch release containing a fix for POOL-149, a regression
+introduced in version 1.5.">
+    <action dev="markt" type="fix" issue="POOL-149" due-to="Shuyang Zhou">
+      Fix case where a thread could end up waiting indefinitely even if objects
+      were available. Also fixes a couple of leaks in the internal processing
+      object count that could lead to pool exhaustion.
+    </action>
+  </release>
+  <release version="1.5.2" date="2009-07-12" description=
+"This is a patch release containing fixes for POOL-146 and POOL-147, regressions
+introduced in version 1.5.">
+    <action dev="markt" type="fix" issue="POOL-146">
+      Handle the case where one key has reached maxActive but other keys have not.
+      Prior to the fix for this issue, threads waiting on objects from keyed pools
+      still having instances available could be blocked by a thread requesting an
+      instance from an exhausted pool.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-147" due-to="Giambattista Bloisi">
+      Fix case where a thread could end up waiting indefinitely even if objects
+      were available.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-145">
+      Ensure that the GenericKeyedObjectPool idle object evictor does not visit the
+      same instance more than once per eviction run.
+    </action>
+  </release>
+  <release version="1.5.1" date="2009-06-16" description=
+"This is a patch release containing a fix for POOL-144, a regression introduced
+in version 1.5.">
+    <action dev="markt" type="fix" issue="POOL-144">
+      When exhausted action is set to WHEN_EXHAUSTED_BLOCK, maxwait is positive and
+      client threads time out waiting for idle objects, capacity can be "leaked"
+      from GenericObjectPools and GeneritCkeyedObjectPools. 
+    </action>
+  </release>
+  <release version="1.5" date="2009-06-10" description=
+"This is a maintenance release including several important bug fixes. This release
+is source and binary compatible with versions 1.3 and 1.4 of commons pool. In addition
+to resolving some important concurrency-related bugs (POOL-135, POOL-125, POOL-29
+POOL-107) this release implements a fairness algorithm to ensure that threads waiting
+for available object instances from GenericObjectPools and GenericKeyedObjectPools are served
+in request arrival order.">
+    <action dev="markt" type="fix" issue="POOL-139" due-to="Sebastian Bazley">
+      StackKeyedObjectPool.getNumActive() needs to be synchronized.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-137" due-to="Sebastian Bazley">
+      Inconsistent synchronization in GenericObjectPool; constant fields should be final.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-75" due-to="Takayuki Kaneko and Gordon Mohr">
+       GenericObjectPool not FIFO with respect to borrowing threads.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-135">
+      _numActive > _maxActive under load
+    </action>
+    <action dev="markt" type="fix" issue="POOL-125">
+      Insufficient control over concurrent access to pooled objects by Evictor, client threads.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-107">
+      Number of connections created has crossed more than maxActive.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-122">
+      java.util.Timer in EvictionTimer does not recover from OutOfMemoryError in Evictor.
+    </action>
+    <action dev="markt" type="fix" issue="POOL-133">
+      Failed object creation may result in invalid active count in GKOP.
+    </action>
+  </release>
+  <release version="1.4" date="2008-01-15" description=
+"This is a maintenance release including numerous bug fixes. This release
+is source and binary compatible with version 1.3 of commons pool, but
+there are some behavior changes introduced to resolve bugs, remove ambiguity
+or improve robustness. Among these are the change in default behavior of
+GenericObjectPool and GenericKeyedObjectPool from FIFO back to LIFO queues
+(as in 1.2 and earlier). The LIFO/FIFO behavior is now configurable. Some of
+the extra synchronization included in version 1.3 was removed / refactored to
+improve performance. The javadoc has also been made more complete and explicit.
+See the detailed list of changes below for specifics on fixed bugs and behavior
+changes in Commons Pool 1.4. This version of Commons Pool depends only on
+JDK version 1.3 or higher. Classes implementing pools are all intended to be
+threadsafe.">
+      <action dev="sandymac" type="fix">
+        Fixed constructor which was ignoring maxTotal parameter: 
+          GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
+          long maxWait, int maxIdle, int maxTotal, boolean testOnBorrow, boolean testOnReturn,
+          long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
+          boolean testWhileIdle)
+      </action>
+      <action dev="sandymac" type="fix">
+        Changed StackKeyedObjectPool to discard stalest, not freshest, idle object when maxSleeping is reached.
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-102" due-to="John Sumsion">
+        Allowed blocked threads in GenericObjectPool borrowObject to be interrupted.
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-86">
+        Fixes to address idle object eviction and LIFO/FIFO behavior reported
+        in POOL-86. Made LIFO/FIFO behavior configurable for GenericObjectPool and
+        GenericKeyedObjectPool, with default set back to LIFO (reverting to 1.2 behavior).
+        Fixed GOP, GKOP evict method and added tests to ensure objects are visited in
+        oldest-to-youngest order. Changed backing store for GOP, GKOP pools back to Commons
+        Collections CursorableLinkedList (brought this class in, repackaged with package scope).
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-110" due-to="Alexander Pogrebnyak">
+        Changed the default setting for Config.softMinEvictableIdleTimeMillis to
+        GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS (was
+        being incorrectly defaulted to DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS).
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-97" due-to="Mark Thomas">
+         Added a wrapper for the shared idle object eviction timer for all
+         pools. The wrapper class wraps the Timer and keeps track of how many
+         pools are using it. If no pools are using the timer, it is canceled.
+         This prevents a thread being left running which, in application server
+         environments, can lead to memory leaks and/or prevent applications
+         from shutting down or reloading cleanly.
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-93"  
+      due-to="Mark Thomas">
+        Reduced synchronization in GenericObjectPool, GenericKeyedObjectPool.
+        Factory method activations within synchronized blocks were causing
+        performance problems in DBCP and other applications where factory
+        methods could block. Fixes both POOL-93 and POOL-108.
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-113">
+        Made _testOnBorrow, _testOnReturn volatile and removed synchronization
+        in associated getters and setters in GenericObjectPool,
+        GenericKeyedObjectPool. Made getNumIdle synchronized in
+        StackKeyedObjectPool. 
+      </action>
+      <action dev="psteitz" type="fix" issue="POOL-116">
+        Fixed an error in the GenericKeyedObjectPool constructor that takes
+        a Config instance as a parameter. The minIdle setting in the Config
+        was being ignored by the constructor.
+      </action>
+      <action def="psteitz" type="fix" issue="POOL-117">
+        Made behavior on instance validation failure consistent across pools,
+        eliminating possible infinite loops in StackObjectPool,
+        StackKeyedObjectPool, SoftReferenceObjectPool when factory fails to
+        create valid objects. 
+      </action>
+      <action dev="sandymac" type="update">
+        When no factory has been defined, addObject now throws 
+        IllegalStateExecption instead of NullPointerException for all pools.
+      </action>
+      <action dev="sandymac" type="update">
+        SoftReferenceObjectPool. Improved the accuracy of getNumIdle by
+        "pruning" references to objects that have been garbage collected.
+      </action>
+      <action dev="sandymac" type="update">
+        GenericObjectPool, GenericKeyedObjectPool, SoftReferenceObjectPool,
+        StackObjectPool. Eliminated IllegalStateExceptions when the following
+        operations are attempted on a closed pool: getNumActive, getNumIdle, 
+        returnObject, invalidateObject.  In each case, the operation is allowed
+        to proceed, reporting the state of the pool that is being shut down, or
+        destroying objects returning to the closed pool.
+      </action>
+      <action dev="sandymac" type="update">
+        StackObjectPool, SoftReferenceObjectPool, GenericKeyedObjectPool. Allowed
+        borrowObject to continue (either examining additional idle instances or
+        with makeObject) when an exception is encountered activating an idle
+        object instead of propagating the exception to the client.  Also made
+        addObject propagate (not swallow) exceptions when passivating newly
+        created instances. 
+      </action>
+      <action dev="psteitz" type="update">
+        StackKeyedObjectPool. Added validation check for objects returned
+        from borrowObject. 
+      </action>
+      <action dev="sandymac" type="update">
+        BaseObjectPool, BaseKeyedObjectPool. Instead of throwing 
+        UnsupportedOperationException, the base class implementations
+        of getNumIdle and getNumActive return negative values. The
+        base implementation of close in BaseObjectPool no longer throws
+        IllegalStateException when invoked on an already closed pool.
+      </action>
+      
+    </release>
+
+    <release version="1.3" date="2006-04-06" description="1.x bugfix release">
+      <action type="fix">A large number of bug fixes. See release notes for changes.</action>
+    </release>
+
+    <release version="1.2" date="2004-06-07" description="bugfixes">
+      <action dev="dirkv" type="fix">
+           GenericKeyedObjectPoolFactory Config Constructor is incorrect
+      </action>
+      <action dev="dirkv" type="fix">
+        Not possible to extend GenericObjectPool.returnObject() without affecting addObject()
+      </action>
+    </release>
+
+    <release version="1.1" date="2003-10-20" description="bugfixes">
+      <action type="fix">A lot of corner cases were fixed</action>
+      <action type="fix">Performance improvement by optimizing pool synchronization, the critical code paths were optimized by reducing pool synchronization but we also added more synchronization where needed</action>
+      <action type="fix">New minIdle feature: the minimum number of objects allowed in the pool before the evictor thread (if active) spawns new objects. (Note no objects are created when: numActive + numIdle >= maxActive)</action>
+      <action type="fix">New maxTotal feature: a cap on the total number of instances controlled by a pool. Only for GenericKeyedObjectPool where maxActive is a cap on the number of active instances from the pool (per key).</action>
+      <action type="fix">UML Class and sequence diagrams</action>
+      <action type="fix">See bugzilla for more changes</action>
+    </release>
+
+    <release version="1.0.1" date="2002-08-12">
+      <action type="fix">No change log available.</action>
+    </release>
+
+    <release version="1.0" date="2002-05-04">
+      <action type="add">No change log available.</action>
+    </release>
+  </body>
+</document>
diff --git a/src/java/org/apache/commons/pool/BaseKeyedObjectPool.java b/src/java/org/apache/commons/pool/BaseKeyedObjectPool.java
new file mode 100644
index 0000000..38aaf0f
--- /dev/null
+++ b/src/java/org/apache/commons/pool/BaseKeyedObjectPool.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple base implementation of <code>KeyedObjectPool</code>.
+ * Optional operations are implemented to either do nothing, return a value
+ * indicating it is unsupported or throw {@link UnsupportedOperationException}.
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public abstract class BaseKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
+    
+    /**
+     * {@inheritDoc}
+     */
+    public abstract V borrowObject(K key) throws Exception;
+    
+    /**
+     * {@inheritDoc}
+     */
+    public abstract void returnObject(K key, V obj) throws Exception;
+    
+    /**
+     * <p>Invalidates an object from the pool.</p>
+     * 
+     * <p>By contract, <code>obj</code> <strong>must</strong> have been obtained
+     * using {@link #borrowObject borrowObject} using a <code>key</code> that is
+     * equivalent to the one used to borrow the <code>Object</code> in the first place.</p>
+     *
+     * <p>This method should be used when an object that has been borrowed
+     * is determined (due to an exception or other problem) to be invalid.</p>
+     *
+     * @param key the key used to obtain the object
+     * @param obj a {@link #borrowObject borrowed} instance to be returned.
+     * @throws Exception 
+     */
+    public abstract void invalidateObject(K key, V obj) throws Exception;
+
+    /**
+     * Not supported in this base implementation.
+     * Always throws an {@link UnsupportedOperationException},
+     * subclasses should override this behavior.
+     * @param key ignored
+     * @throws UnsupportedOperationException
+     */
+    public void addObject(K key) throws Exception, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @return a negative value.
+     * @param key ignored
+     */
+    public int getNumIdle(K key) throws UnsupportedOperationException {
+        return -1;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @return a negative value.
+     * @param key ignored
+     */
+    public int getNumActive(K key) throws UnsupportedOperationException {
+        return -1;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @return a negative value.
+     */
+    public int getNumIdle() throws UnsupportedOperationException {
+        return -1;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @return a negative value.
+     */
+    public int getNumActive() throws UnsupportedOperationException {
+        return -1;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @throws UnsupportedOperationException
+     */
+    public void clear() throws Exception, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @param key ignored
+     * @throws UnsupportedOperationException
+     */
+    public void clear(K key) throws Exception, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Close this pool.
+     * This affects the behavior of <code>isClosed</code> and <code>assertOpen</code>.
+     */
+    public void close() throws Exception {
+        closed = true;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * Always throws an {@link UnsupportedOperationException},
+     * subclasses should override this behavior.
+     * @param factory the new KeyedPoolableObjectFactory
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    public void setFactory(KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Has this pool instance been closed.
+     * @return <code>true</code> when this pool has been closed.
+     * @since Pool 1.4
+     */
+    protected final boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * Throws an <code>IllegalStateException</code> when this pool has been closed.
+     * @throws IllegalStateException when this pool has been closed.
+     * @see #isClosed()
+     * @since Pool 1.4
+     */
+    protected final void assertOpen() throws IllegalStateException {
+        if(isClosed()) {
+            throw new IllegalStateException("Pool not open");
+        }
+    }
+
+    /** Whether or not the pool is close */
+    private volatile boolean closed = false;
+}
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..a0c7883
--- /dev/null
+++ b/src/java/org/apache/commons/pool/BaseKeyedPoolableObjectFactory.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A base implementation of <code>KeyedPoolableObjectFactory</code>.
+ * <p>
+ * All operations defined here are essentially no-op's.
+ * </p>
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @see KeyedPoolableObjectFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222388 $ $Date: 2011-12-22 13:28:27 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public abstract class BaseKeyedPoolableObjectFactory<K, V> implements KeyedPoolableObjectFactory<K, V> {
+    /**
+     * Create an instance that can be served by the pool.
+     *
+     * @param key the key used when constructing the object
+     * @return an instance that can be served by the pool
+     */
+    public abstract V makeObject(K key)
+        throws Exception;
+
+    /**
+     * Destroy an instance no longer needed by the pool.
+     * <p>
+     * The default implementation is a no-op.
+     * </p>
+     *
+     * @param key the key used when selecting the instance
+     * @param obj the instance to be destroyed
+     */
+    public void destroyObject(K key, V obj)
+        throws Exception {
+    }
+
+    /**
+     * Ensures that the instance is safe to be returned by the pool.
+     * <p>
+     * The default implementation always returns <tt>true</tt>.
+     * </p>
+     *
+     * @param key the key used when selecting the object
+     * @param obj the instance to be validated
+     * @return always <code>true</code> in the default implementation
+     */ 
+    public boolean validateObject(K key, V obj) {
+        return true;
+    }
+
+    /**
+     * Reinitialize an instance to be returned by the pool.
+     * <p>
+     * The default implementation is a no-op.
+     * </p>
+     *
+     * @param key the key used when selecting the object
+     * @param obj the instance to be activated
+     */
+    public void activateObject(K key, V obj)
+        throws Exception {
+    }
+
+    /**
+     * Uninitialize an instance to be returned to the idle object pool.
+     * <p>
+     * The default implementation is a no-op.
+     * </p>
+     *
+     * @param key the key used when selecting the object
+     * @param obj the instance to be passivated
+     */
+    public void passivateObject(K key, V obj)
+        throws Exception {
+    }
+}
diff --git a/src/java/org/apache/commons/pool/BaseObjectPool.java b/src/java/org/apache/commons/pool/BaseObjectPool.java
new file mode 100644
index 0000000..a520a87
--- /dev/null
+++ b/src/java/org/apache/commons/pool/BaseObjectPool.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A simple base implementation of {@link ObjectPool}.
+ * Optional operations are implemented to either do nothing, return a value
+ * indicating it is unsupported or throw {@link UnsupportedOperationException}.
+ *
+ * @param <T> the type of objects held in this pool
+ *
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public abstract class BaseObjectPool<T> implements ObjectPool<T> {
+    /**
+     * Obtains an instance from the pool.
+     * 
+     * @return an instance from the pool
+     * @throws Exception if an instance cannot be obtained from the pool
+     */
+    public abstract T borrowObject() throws Exception;
+    
+    /**
+     * Returns an instance to the pool.
+     * 
+     * @param obj instance to return to the pool
+     */
+    public abstract void returnObject(T obj) throws Exception;
+    
+    /**
+     * <p>Invalidates an object from the pool.</p>
+     * 
+     * <p>By contract, <code>obj</code> <strong>must</strong> have been obtained
+     * using {@link #borrowObject borrowObject}.<p>
+     * 
+     * <p>This method should be used when an object that has been borrowed
+     * is determined (due to an exception or other problem) to be invalid.</p>
+     *
+     * @param obj a {@link #borrowObject borrowed} instance to be disposed.
+     * @throws Exception 
+     */
+    public abstract void invalidateObject(T obj) throws Exception;
+
+    /**
+     * Not supported in this base implementation.
+     * @return a negative value.
+     * 
+     * @throws UnsupportedOperationException
+     */
+    public int getNumIdle() throws UnsupportedOperationException {
+        return -1;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * @return a negative value.
+     * 
+     * @throws UnsupportedOperationException
+     */
+    public int getNumActive() throws UnsupportedOperationException {
+        return -1;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * 
+     * @throws UnsupportedOperationException
+     */
+    public void clear() throws Exception, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * Always throws an {@link UnsupportedOperationException},
+     * subclasses should override this behavior.
+     * 
+     * @throws UnsupportedOperationException
+     */
+    public void addObject() throws Exception, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Close this pool.
+     * This affects the behavior of <code>isClosed</code> and <code>assertOpen</code>.
+     */
+    public void close() throws Exception {
+        closed = true;
+    }
+
+    /**
+     * Not supported in this base implementation.
+     * Always throws an {@link UnsupportedOperationException},
+     * subclasses should override this behavior.
+     * 
+     * @param factory the PoolableObjectFactory
+     * @throws UnsupportedOperationException
+     * @throws IllegalStateException
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    public void setFactory(PoolableObjectFactory<T> factory) throws IllegalStateException, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Has this pool instance been closed.
+     * @return <code>true</code> when this pool has been closed.
+     */
+    public final boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * Throws an <code>IllegalStateException</code> when this pool has been closed.
+     * @throws IllegalStateException when this pool has been closed.
+     * @see #isClosed()
+     */
+    protected final void assertOpen() throws IllegalStateException {
+        if (isClosed()) {
+            throw new IllegalStateException("Pool not open");
+        }
+    }
+
+    /** Whether or not the pool is closed */
+    private volatile boolean closed = false;
+}
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..0f1f862
--- /dev/null
+++ b/src/java/org/apache/commons/pool/BasePoolableObjectFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A base implementation of <code>PoolableObjectFactory</code>.
+ * <p>
+ * All operations defined here are essentially no-op's.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @see PoolableObjectFactory
+ * @see BaseKeyedPoolableObjectFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222388 $ $Date: 2011-12-22 13:28:27 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public abstract class BasePoolableObjectFactory<T> implements PoolableObjectFactory<T> {
+    
+    /**
+     * {@inheritDoc}
+     */
+    public abstract T makeObject() throws Exception;
+
+    /**
+     *  No-op.
+     *  
+     *  @param obj ignored
+     */
+    public void destroyObject(T obj)
+        throws Exception  {
+    }
+
+    /**
+     * This implementation always returns <tt>true</tt>.
+     * 
+     * @param obj ignored
+     * @return <tt>true</tt>
+     */
+    public boolean validateObject(T obj) {
+        return true;
+    }
+
+    /**
+     *  No-op.
+     *  
+     *  @param obj ignored
+     */
+    public void activateObject(T obj) throws Exception {
+    }
+
+    /**
+     *  No-op.
+     *  
+     * @param obj ignored
+     */
+    public void passivateObject(T obj)
+        throws Exception {
+    }
+}
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..40f0dc6
--- /dev/null
+++ b/src/java/org/apache/commons/pool/KeyedObjectPool.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A "keyed" pooling interface.
+ * <p>
+ * A keyed pool pools instances of multiple types. Each
+ * type may be accessed using an arbitrary key.
+ * </p>
+ * <p>
+ * Example of use:
+ * <pre style="border:solid thin; padding: 1ex;"
+ * > Object obj = <code style="color:#00C">null</code>;
+ * Object key = <code style="color:#C00">"Key"</code>;
+ *
+ * <code style="color:#00C">try</code> {
+ *     obj = pool.borrowObject(key);
+ *     <code style="color:#0C0">//...use the object...</code>
+ * } <code style="color:#00C">catch</code>(Exception e) {
+ *     <code style="color:#0C0">// invalidate the object</code>
+ *     pool.invalidateObject(key, obj);
+ *     <code style="color:#0C0">// do not return the object to the pool twice</code>
+ *     obj = <code style="color:#00C">null</code>;
+ * } <code style="color:#00C">finally</code> {
+ *     <code style="color:#0C0">// make sure the object is returned to the pool</code>
+ *     <code style="color:#00C">if</code>(<code style="color:#00C">null</code> != obj) {
+ *         pool.returnObject(key, obj);
+ *     }
+ * }</pre>
+ * </p>
+ * <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 {@link java.util.Map Map} of
+ * {@link ObjectPool pools}).
+ * </p>
+ *
+ * <p>See {@link BaseKeyedObjectPool} for a simple base implementation.</p>
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @see KeyedPoolableObjectFactory
+ * @see KeyedObjectPoolFactory
+ * @see ObjectPool
+ * @see BaseKeyedObjectPool
+ * @since Pool 1.0
+ */
+public interface KeyedObjectPool<K, V> {
+    /**
+     * Obtains an instance from this pool for the specified <code>key</code>.
+     * <p>
+     * Instances returned from this method will have been either newly created with
+     * {@link KeyedPoolableObjectFactory#makeObject makeObject} or will be a previously idle object and
+     * have been activated with {@link KeyedPoolableObjectFactory#activateObject activateObject} and
+     * then validated with {@link KeyedPoolableObjectFactory#validateObject validateObject}.
+     * <p>
+     * By contract, clients <strong>must</strong> return the borrowed object using
+     * {@link #returnObject returnObject}, {@link #invalidateObject invalidateObject}, or a related method
+     * as defined in an implementation or sub-interface,
+     * using a <code>key</code> that is {@link Object#equals equivalent} to the one used to
+     * borrow the instance in the first place.
+     * <p>
+     * The behaviour of this method when the pool has been exhausted
+     * is not strictly specified (although it may be specified by implementations).
+     * Older versions of this method would return <code>null</code> to indicate exhaustion,
+     * newer versions are encouraged to throw a {@link NoSuchElementException}.
+     *
+     * @param key the key used to obtain the object
+     * @return an instance from this pool.
+     * @throws IllegalStateException after {@link #close close} has been called on this pool
+     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject makeObject} throws an exception
+     * @throws NoSuchElementException when the pool is exhausted and cannot or will not return another instance
+     */
+    V borrowObject(K key) throws Exception, NoSuchElementException, IllegalStateException;
+
+    /**
+     * Return an instance to the pool.
+     * By contract, <code>obj</code> <strong>must</strong> have been obtained
+     * using {@link #borrowObject borrowObject}
+     * or a related method as defined in an implementation
+     * or sub-interface
+     * using a <code>key</code> that is equivalent to the one used to
+     * borrow the instance in the first place.
+     *
+     * @param key the key used to obtain the object
+     * @param obj a {@link #borrowObject borrowed} instance to be returned.
+     * @throws Exception 
+     */
+    void returnObject(K key, V obj) throws Exception;
+
+    /**
+     * <p>Invalidates an object from the pool.</p>
+     * 
+     * <p>By contract, <code>obj</code> <strong>must</strong> have been obtained
+     * using {@link #borrowObject borrowObject} or a related method as defined
+     * in an implementation or sub-interface using a <code>key</code> that is
+     * equivalent to the one used to borrow the <code>Object</code> in the first place.</p>
+     *
+     * <p>This method should be used when an object that has been borrowed
+     * is determined (due to an exception or other problem) to be invalid.</p>
+     *
+     * @param key the key used to obtain the object
+     * @param obj a {@link #borrowObject borrowed} instance to be returned.
+     * @throws Exception 
+     */
+    void invalidateObject(K key, V obj) throws Exception;
+
+    /**
+     * Create an object using the {@link KeyedPoolableObjectFactory factory} or other
+     * implementation dependent mechanism, passivate it, and then place it in the idle object pool.
+     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects
+     * (Optional operation).
+     *
+     * @param key the key a new instance should be added to
+     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
+     * @throws IllegalStateException after {@link #close} has been called on this pool.
+     * @throws UnsupportedOperationException when this pool cannot add new idle objects.
+     */
+    void addObject(K key) throws Exception, IllegalStateException, UnsupportedOperationException;
+
+    /**
+     * Returns the number of instances
+     * corresponding to the given <code>key</code>
+     * currently idle in this pool (optional operation).
+     * Returns a negative value if this information is not available.
+     *
+     * @param key the key to query
+     * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool or a negative value if unsupported
+     * @throws UnsupportedOperationException <strong>deprecated</strong>: when this implementation doesn't support the operation
+     */
+    int getNumIdle(K key) throws UnsupportedOperationException;
+
+    /**
+     * Returns the number of instances
+     * currently borrowed from but not yet returned
+     * to the pool corresponding to the
+     * given <code>key</code> (optional operation).
+     * Returns a negative value if this information is not available.
+     *
+     * @param key the key to query
+     * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool or a negative value if unsupported
+     * @throws UnsupportedOperationException <strong>deprecated</strong>: when this implementation doesn't support the operation
+     */
+    int getNumActive(K key) throws UnsupportedOperationException;
+
+    /**
+     * Returns the total number of instances
+     * currently idle in this pool (optional operation).
+     * Returns a negative value if this information is not available.
+     *
+     * @return the total number of instances currently idle in this pool or a negative value if unsupported
+     * @throws UnsupportedOperationException <strong>deprecated</strong>: when this implementation doesn't support the operation
+     */
+    int getNumIdle() throws UnsupportedOperationException;
+
+    /**
+     * Returns the total number of instances
+     * current borrowed from this pool but not
+     * yet returned (optional operation).
+     * Returns a negative value if this information is not available.
+     *
+     * @return the total number of instances currently borrowed from this pool or a negative value if unsupported
+     * @throws UnsupportedOperationException <strong>deprecated</strong>: when this implementation doesn't support the operation
+     */
+    int getNumActive() throws UnsupportedOperationException;
+
+    /**
+     * Clears the pool, removing all pooled instances (optional operation).
+     * Throws {@link UnsupportedOperationException} if the pool cannot be cleared.
+     *
+     * @throws UnsupportedOperationException when this implementation doesn't support the operation
+     */
+    void clear() throws Exception, UnsupportedOperationException;
+
+    /**
+     * Clears the specified pool, removing all
+     * pooled instances corresponding to
+     * the given <code>key</code> (optional operation).
+     * Throws {@link UnsupportedOperationException} if the pool cannot be cleared.
+     *
+     * @param key the key to clear
+     * @throws UnsupportedOperationException when this implementation doesn't support the operation
+     */
+    void clear(K key) throws Exception, UnsupportedOperationException;
+
+    /**
+     * Close this pool, and free any resources associated with it.
+     * <p>
+     * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking
+     * this method on a pool will cause them to throw an {@link IllegalStateException}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    void close() throws Exception;
+
+    /**
+     * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses
+     * to create new instances (optional operation).
+     * Trying to change the <code>factory</code> after a pool has been used will frequently
+     * throw an {@link UnsupportedOperationException}. It is up to the pool
+     * implementation to determine when it is acceptable to call this method.
+     *
+     * @param factory the {@link KeyedPoolableObjectFactory} used to create new instances.
+     * @throws IllegalStateException when the factory cannot be set at this time
+     * @throws UnsupportedOperationException when this implementation doesn't support the operation
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    void setFactory(KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException, UnsupportedOperationException;
+}
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..712cd73
--- /dev/null
+++ b/src/java/org/apache/commons/pool/KeyedObjectPoolFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A factory for creating {@link KeyedObjectPool}s.
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @see KeyedObjectPool
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222388 $ $Date: 2011-12-22 13:28:27 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public interface KeyedObjectPoolFactory<K, V> {
+    /**
+     * Create a new {@link KeyedObjectPool}.
+     * @return a new {@link KeyedObjectPool}
+     * @throws IllegalStateException when this pool factory is not configured properly
+     */
+    KeyedObjectPool<K, V> createPool() throws IllegalStateException;
+}
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..10f9d75
--- /dev/null
+++ b/src/java/org/apache/commons/pool/KeyedPoolableObjectFactory.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * An interface defining life-cycle methods for
+ * instances to be served by a {@link KeyedObjectPool}.
+ * <p>
+ * By contract, when an {@link KeyedObjectPool}
+ * delegates to a {@link KeyedPoolableObjectFactory},
+ * <ol>
+ *  <li>
+ *   {@link #makeObject makeObject}
+ *   is called whenever a new instance is needed.
+ *  </li>
+ *  <li>
+ *   {@link #activateObject activateObject}
+ *   is invoked on every instance that has been
+ *   {@link #passivateObject passivated} before it is
+ *   {@link KeyedObjectPool#borrowObject borrowed} from the pool.
+ *  </li>
+ *  <li>
+ *   {@link #validateObject validateObject}
+ *   is invoked on {@link #activateObject activated} instances to make sure
+ *   they can be {@link KeyedObjectPool#borrowObject borrowed} from the pool.
+ *   <code>validateObject</code> <strong>may</strong> also be used to test an
+ *   instance being {@link KeyedObjectPool#returnObject returned} to the pool
+ *   before it is {@link #passivateObject passivated}. It will only be invoked
+ *   on an activated instance.
+ *  </li>
+ *  <li>
+ *   {@link #passivateObject passivateObject}
+ *   is invoked on every instance when it is returned to the pool.
+ *  </li>
+ *  <li>
+ *   {@link #destroyObject destroyObject}
+ *   is invoked on every instance when it is being "dropped" from the
+ *   pool (whether due to the response from <code>validateObject</code>,
+ *   or for reasons specific to the pool implementation.) There is no
+ *   guarantee that the instance being destroyed will
+ *   be considered active, passive or in a generally consistent state.
+ *  </li>
+ * </ol>
+ * </p>
+ * <p>
+ * {@link KeyedPoolableObjectFactory} must be thread-safe. The only promise
+ * an {@link KeyedObjectPool} makes is that the same instance of an object will not
+ * be passed to more than one method of a <code>KeyedPoolableObjectFactory</code>
+ * at a time.
+ * </p>
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @see KeyedObjectPool
+ *
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222388 $ $Date: 2011-12-22 13:28:27 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public interface KeyedPoolableObjectFactory<K, V> {
+    /**
+     * Create an instance that can be served by the pool.
+     *
+     * @param key the key used when constructing the object
+     * @return an instance that can be served by the pool.
+     * @throws Exception if there is a problem creating a new instance,
+     *    this will be propagated to the code requesting an object.
+     */
+    V makeObject(K key) throws Exception;
+
+    /**
+     * Destroy an instance no longer needed by the pool.
+     * <p>
+     * It is important for implementations of this method to be aware
+     * that there is no guarantee about what state <code>obj</code>
+     * will be in and the implementation should be prepared to handle
+     * unexpected errors.
+     * </p>
+     * <p>
+     * Also, an implementation must take in to consideration that
+     * instances lost to the garbage collector may never be destroyed.
+     * </p>
+     *
+     * @param key the key used when selecting the instance
+     * @param obj the instance to be destroyed
+     * @throws Exception should be avoided as it may be swallowed by
+     *    the pool implementation.
+     * @see #validateObject
+     * @see KeyedObjectPool#invalidateObject
+     */
+    void destroyObject(K key, V obj) throws Exception;
+
+    /**
+     * Ensures that the instance is safe to be returned by the pool.
+     * Returns <code>false</code> if <code>obj</code> should be destroyed.
+     *
+     * @param key the key used when selecting the object
+     * @param obj the instance to be validated
+     * @return <code>false</code> if <code>obj</code> is not valid and should
+     *         be dropped from the pool, <code>true</code> otherwise.
+     */
+    boolean validateObject(K key, V obj);
+
+    /**
+     * Reinitialize an instance to be returned by the pool.
+     *
+     * @param key the key used when selecting the object
+     * @param obj the instance to be activated
+     * @throws Exception if there is a problem activating <code>obj</code>,
+     *    this exception may be swallowed by the pool.
+     * @see #destroyObject
+     */
+    void activateObject(K key, V obj) throws Exception;
+
+    /**
+     * Uninitialize an instance to be returned to the idle object pool.
+     *
+     * @param key the key used when selecting the object
+     * @param obj the instance to be passivated
+     * @throws Exception if there is a problem passivating <code>obj</code>,
+     *    this exception may be swallowed by the pool.
+     * @see #destroyObject
+     */
+    void passivateObject(K key, V obj) throws Exception;
+}
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..b7add78
--- /dev/null
+++ b/src/java/org/apache/commons/pool/ObjectPool.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A pooling interface.
+ * <p>
+ * <code>ObjectPool</code> defines a trivially simple pooling interface. The only
+ * required methods are {@link #borrowObject borrowObject}, {@link #returnObject returnObject}
+ * and {@link #invalidateObject invalidateObject}.
+ * </p>
+ * <p>
+ * Example of use:
+ * <pre style="border:solid thin; padding: 1ex;"
+ * > Object obj = <code style="color:#00C">null</code>;
+ *
+ * <code style="color:#00C">try</code> {
+ *     obj = pool.borrowObject();
+ *     <code style="color:#00C">try</code> {
+ *         <code style="color:#0C0">//...use the object...</code>
+ *     } <code style="color:#00C">catch</code>(Exception e) {
+ *         <code style="color:#0C0">// invalidate the object</code>
+ *         pool.invalidateObject(obj);
+ *         <code style="color:#0C0">// do not return the object to the pool twice</code>
+ *         obj = <code style="color:#00C">null</code>;
+ *     } <code style="color:#00C">finally</code> {
+ *         <code style="color:#0C0">// make sure the object is returned to the pool</code>
+ *         <code style="color:#00C">if</code>(<code style="color:#00C">null</code> != obj) {
+ *             pool.returnObject(obj);
+ *        }
+ *     }
+ * } <code style="color:#00C">catch</code>(Exception e) {
+ *       <code style="color:#0C0">// failed to borrow an object</code>
+ * }</pre>
+ * </p>
+ *
+ * <p>See {@link BaseObjectPool} for a simple base implementation.</p>
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @see PoolableObjectFactory
+ * @see ObjectPoolFactory
+ * @see KeyedObjectPool
+ * @see BaseObjectPool
+ * @since Pool 1.0
+ */
+public interface ObjectPool<T> {
+    /**
+     * Obtains an instance from this pool.
+     * <p>
+     * Instances returned from this method will have been either newly created with
+     * {@link PoolableObjectFactory#makeObject makeObject} or will be a previously idle object and
+     * have been activated with {@link PoolableObjectFactory#activateObject activateObject} and
+     * then validated with {@link PoolableObjectFactory#validateObject validateObject}.
+     * </p>
+     * <p>
+     * By contract, clients <strong>must</strong> return the borrowed instance using
+     * {@link #returnObject returnObject}, {@link #invalidateObject invalidateObject}, or a related method
+     * as defined in an implementation or sub-interface.
+     * </p>
+     * <p>
+     * The behaviour of this method when the pool has been exhausted
+     * is not strictly specified (although it may be specified by implementations).
+     * Older versions of this method would return <code>null</code> to indicate exhaustion,
+     * newer versions are encouraged to throw a {@link NoSuchElementException}.
+     * </p>
+     *
+     * @return an instance from this pool.
+     * @throws IllegalStateException after {@link #close close} has been called on this pool.
+     * @throws Exception when {@link PoolableObjectFactory#makeObject makeObject} throws an exception.
+     * @throws NoSuchElementException when the pool is exhausted and cannot or will not return another instance.
+     */
+    T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
+
+    /**
+     * Return an instance to the pool.
+     * By contract, <code>obj</code> <strong>must</strong> have been obtained
+     * using {@link #borrowObject() borrowObject}
+     * or a related method as defined in an implementation
+     * or sub-interface.
+     *
+     * @param obj a {@link #borrowObject borrowed} instance to be returned.
+     * @throws Exception 
+     */
+    void returnObject(T obj) throws Exception;
+
+    /**
+     * <p>Invalidates an object from the pool.</p>
+     * 
+     * <p>By contract, <code>obj</code> <strong>must</strong> have been obtained
+     * using {@link #borrowObject borrowObject} or a related method as defined in
+     * an implementation or sub-interface.</p>
+     *
+     * <p>This method should be used when an object that has been borrowed
+     * is determined (due to an exception or other problem) to be invalid.</p>
+     *
+     * @param obj a {@link #borrowObject borrowed} instance to be disposed.
+     * @throws Exception
+     */
+    void invalidateObject(T obj) throws Exception;
+
+    /**
+     * Create an object using the {@link PoolableObjectFactory factory} or other
+     * implementation dependent mechanism, passivate it, and then place it in the idle object pool.
+     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
+     * (Optional operation).
+     *
+     * @throws Exception when {@link PoolableObjectFactory#makeObject} fails.
+     * @throws IllegalStateException after {@link #close} has been called on this pool.
+     * @throws UnsupportedOperationException when this pool cannot add new idle objects.
+     */
+    void addObject() throws Exception, IllegalStateException, UnsupportedOperationException;
+
+    /**
+     * Return the number of instances
+     * currently idle in this pool (optional operation).
+     * This may be considered an approximation of the number
+     * of objects that can be {@link #borrowObject borrowed}
+     * without creating any new instances.
+     * Returns a negative value if this information is not available.
+     *
+     * @return the number of instances currently idle in this pool or a negative value if unsupported
+     * @throws UnsupportedOperationException <strong>deprecated</strong>: if this implementation does not support the operation
+     */
+    int getNumIdle() throws UnsupportedOperationException;
+
+    /**
+     * Return the number of instances
+     * currently borrowed from this pool
+     * (optional operation).
+     * Returns a negative value if this information is not available.
+     *
+     * @return the number of instances currently borrowed from this pool or a negative value if unsupported
+     * @throws UnsupportedOperationException <strong>deprecated</strong>: if this implementation does not support the operation
+     */
+    int getNumActive() throws UnsupportedOperationException;
+
+    /**
+     * Clears any objects sitting idle in the pool, releasing any
+     * associated resources (optional operation).
+     * Idle objects cleared must be {@link PoolableObjectFactory#destroyObject(Object) destroyed}.
+     *
+     * @throws UnsupportedOperationException if this implementation does not support the operation
+     */
+    void clear() throws Exception, UnsupportedOperationException;
+
+    /**
+     * Close this pool, and free any resources associated with it.
+     * <p>
+     * Calling {@link #addObject} or {@link #borrowObject} after invoking
+     * this method on a pool will cause them to throw an
+     * {@link IllegalStateException}.
+     * </p>
+     *
+     * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed.
+     */
+    void close() throws Exception;
+
+    /**
+     * Sets the {@link PoolableObjectFactory factory} this pool uses
+     * to create new instances (optional operation). Trying to change
+     * the <code>factory</code> after a pool has been used will frequently
+     * throw an {@link UnsupportedOperationException}. It is up to the pool
+     * implementation to determine when it is acceptable to call this method.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to create new instances.
+     * @throws IllegalStateException when the factory cannot be set at this time
+     * @throws UnsupportedOperationException if this implementation does not support the operation
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    void setFactory(PoolableObjectFactory<T> 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..4113c68
--- /dev/null
+++ b/src/java/org/apache/commons/pool/ObjectPoolFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * A factory interface for creating {@link ObjectPool}s.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @see ObjectPool
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222388 $ $Date: 2011-12-22 13:28:27 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public interface ObjectPoolFactory<T> {
+    /**
+     * Create and return a new {@link ObjectPool}.
+     * @return a new {@link ObjectPool}
+     * @throws IllegalStateException when this pool factory is not configured properly
+     */
+    ObjectPool<T> createPool() throws IllegalStateException;
+}
diff --git a/src/java/org/apache/commons/pool/PoolUtils.java b/src/java/org/apache/commons/pool/PoolUtils.java
new file mode 100644
index 0000000..db3792b
--- /dev/null
+++ b/src/java/org/apache/commons/pool/PoolUtils.java
@@ -0,0 +1,2476 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class consists exclusively of static methods that operate on or return ObjectPool
+ * or KeyedObjectPool related interfaces.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222670 $ $Date: 2011-12-23 08:18:25 -0500 (Fri, 23 Dec 2011) $
+ * @since Pool 1.3
+ */
+public final class PoolUtils {
+
+    /**
+     * Timer used to periodically check pools idle object count.
+     * Because a {@link Timer} creates a {@link Thread} this is lazily instantiated.
+     */
+    private static Timer MIN_IDLE_TIMER; //@GuardedBy("this")
+
+    /**
+     * PoolUtils instances should NOT be constructed in standard programming.
+     * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);.
+     * This constructor is public to permit tools that require a JavaBean instance to operate.
+     */
+    public PoolUtils() {
+    }
+
+    /**
+     * Should the supplied Throwable be re-thrown (eg if it is an instance of
+     * one of the Throwables that should never be swallowed). Used by the pool
+     * error handling for operations that throw exceptions that normally need to
+     * be ignored.
+     * @param t The Throwable to check
+     * @throws ThreadDeath if that is passed in
+     * @throws VirtualMachineError if that is passed in
+     * @since Pool 1.5.5
+     */
+    public static void checkRethrow(Throwable t) {
+        if (t instanceof ThreadDeath) {
+            throw (ThreadDeath) t;
+        }
+        if (t instanceof VirtualMachineError) {
+            throw (VirtualMachineError) t;
+        }
+        // All other instances of Throwable will be silently swallowed
+    }
+
+    /**
+     * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
+     * needed. This method is the equivalent of calling
+     * {@link #adapt(KeyedPoolableObjectFactory, Object) PoolUtils.adapt(aKeyedPoolableObjectFactory, new Object())}.
+     *
+     * @param <V> the type of object
+     * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
+     * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with an internal key.
+     * @throws IllegalArgumentException when <code>keyedFactory</code> is <code>null</code>.
+     * @see #adapt(KeyedPoolableObjectFactory, Object)
+     * @since Pool 1.3
+     */
+    public static <V> PoolableObjectFactory<V> adapt(final KeyedPoolableObjectFactory<Object, V> keyedFactory) throws IllegalArgumentException {
+        return adapt(keyedFactory, new Object());
+    }
+
+    /**
+     * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
+     * needed using the specified <code>key</code> when delegating.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
+     * @param key the key to use when delegating.
+     * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with the specified key.
+     * @throws IllegalArgumentException when <code>keyedFactory</code> or <code>key</code> is <code>null</code>.
+     * @see #adapt(KeyedPoolableObjectFactory)
+     * @since Pool 1.3
+     */
+    public static <K, V> PoolableObjectFactory<V> adapt(final KeyedPoolableObjectFactory<K, V> keyedFactory, final K key) throws IllegalArgumentException {
+        return new PoolableObjectFactoryAdaptor<K, V>(keyedFactory, key);
+    }
+
+    /**
+     * Adapt a <code>PoolableObjectFactory</code> instance to work where a <code>KeyedPoolableObjectFactory</code> is
+     * needed. The key is ignored.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param factory the {@link PoolableObjectFactory} to delegate to.
+     * @return a {@link KeyedPoolableObjectFactory} that delegates to <code>factory</code> ignoring the key.
+     * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
+     * @since Pool 1.3
+     */
+    public static <K, V> KeyedPoolableObjectFactory<K, V> adapt(final PoolableObjectFactory<V> factory) throws IllegalArgumentException {
+        return new KeyedPoolableObjectFactoryAdaptor<K, V>(factory);
+    }
+
+    /**
+     * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed. This is the
+     * equivalent of calling {@link #adapt(KeyedObjectPool, Object) PoolUtils.adapt(aKeyedObjectPool, new Object())}.
+     *
+     * @param <V> the type of object
+     * @param keyedPool the {@link KeyedObjectPool} to delegate to.
+     * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with an internal key.
+     * @throws IllegalArgumentException when <code>keyedPool</code> is <code>null</code>.
+     * @see #adapt(KeyedObjectPool, Object)
+     * @since Pool 1.3
+     */
+    public static <V> ObjectPool<V> adapt(final KeyedObjectPool<Object, V> keyedPool) throws IllegalArgumentException {
+        return adapt(keyedPool, new Object());
+    }
+
+    /**
+     * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed using the
+     * specified <code>key</code> when delegating.
+     *
+     * @param <V> the type of object
+     * @param keyedPool the {@link KeyedObjectPool} to delegate to.
+     * @param key the key to use when delegating.
+     * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with the specified key.
+     * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
+     * @see #adapt(KeyedObjectPool)
+     * @since Pool 1.3
+     */
+    public static <V> ObjectPool<V> adapt(final KeyedObjectPool<Object, V> keyedPool, final Object key) throws IllegalArgumentException {
+        return new ObjectPoolAdaptor<V>(keyedPool, key);
+    }
+
+    /**
+     * Adapt an <code>ObjectPool</code> to work where an <code>KeyedObjectPool</code> is needed.
+     * The key is ignored.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param pool the {@link ObjectPool} to delegate to.
+     * @return a {@link KeyedObjectPool} that delegates to <code>pool</code> ignoring the key.
+     * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
+     * @since Pool 1.3
+     */
+    public static <K, V> KeyedObjectPool<K, V> adapt(final ObjectPool<V> pool) throws IllegalArgumentException {
+        return new KeyedObjectPoolAdaptor<K, V>(pool);
+    }
+
+    /**
+     * Wraps an <code>ObjectPool</code> and dynamically checks the type of objects borrowed and returned to the pool.
+     * If an object is passed to the pool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
+     *
+     * @param <T> the type of object
+     * @param pool the pool to enforce type safety on
+     * @param type the class type to enforce.
+     * @return an <code>ObjectPool</code> that will only allow objects of <code>type</code>
+     * @since Pool 1.3
+     */
+    public static <T> ObjectPool<T> checkedPool(final ObjectPool<T> pool, final Class<T> type) {
+        if (pool == null) {
+            throw new IllegalArgumentException("pool must not be null.");
+        }
+        if (type == null) {
+            throw new IllegalArgumentException("type must not be null.");
+        }
+        return new CheckedObjectPool<T>(pool, type);
+    }
+
+    /**
+     * Wraps a <code>KeyedObjectPool</code> and dynamically checks the type of objects borrowed and returned to the keyedPool.
+     * If an object is passed to the keyedPool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the keyedPool to enforce type safety on
+     * @param type the class type to enforce.
+     * @return a <code>KeyedObjectPool</code> that will only allow objects of <code>type</code>
+     * @since Pool 1.3
+     */
+    public static <K, V> KeyedObjectPool<K, V> checkedPool(final KeyedObjectPool<K, V> keyedPool, final Class<V> type) {
+        if (keyedPool == null) {
+            throw new IllegalArgumentException("keyedPool must not be null.");
+        }
+        if (type == null) {
+            throw new IllegalArgumentException("type must not be null.");
+        }
+        return new CheckedKeyedObjectPool<K, V>(keyedPool, type);
+    }
+
+    /**
+     * Periodically check the idle object count for the pool. At most one idle object will be added per period.
+     * If there is an exception when calling {@link ObjectPool#addObject()} then no more checks will be performed.
+     *
+     * @param <T> the type of object
+     * @param pool the pool to check periodically.
+     * @param minIdle if the {@link ObjectPool#getNumIdle()} is less than this then add an idle object.
+     * @param period the frequency to check the number of idle objects in a pool, see
+     *      {@link Timer#schedule(TimerTask, long, long)}.
+     * @return the {@link TimerTask} that will periodically check the pools idle object count.
+     * @throws IllegalArgumentException when <code>pool</code> is <code>null</code> or
+     *      when <code>minIdle</code> is negative or when <code>period</code> isn't
+     *      valid for {@link Timer#schedule(TimerTask, long, long)}.
+     * @since Pool 1.3
+     */
+    public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool, final int minIdle, final long period) throws IllegalArgumentException {
+        if (pool == null) {
+            throw new IllegalArgumentException("keyedPool must not be null.");
+        }
+        if (minIdle < 0) {
+            throw new IllegalArgumentException("minIdle must be non-negative.");
+        }
+        final TimerTask task = new ObjectPoolMinIdleTimerTask<T>(pool, minIdle);
+        getMinIdleTimer().schedule(task, 0L, period);
+        return task;
+    }
+
+    /**
+     * Periodically check the idle object count for the key in the keyedPool. At most one idle object will be added per period.
+     * If there is an exception when calling {@link KeyedObjectPool#addObject(Object)} then no more checks for that key
+     * will be performed.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the keyedPool to check periodically.
+     * @param key the key to check the idle count of.
+     * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
+     * @param period the frequency to check the number of idle objects in a keyedPool, see
+     *      {@link Timer#schedule(TimerTask, long, long)}.
+     * @return the {@link TimerTask} that will periodically check the pools idle object count.
+     * @throws IllegalArgumentException when <code>keyedPool</code>, <code>key</code> is <code>null</code> or
+     *      when <code>minIdle</code> is negative or when <code>period</code> isn't
+     *      valid for {@link Timer#schedule(TimerTask, long, long)}.
+     * @since Pool 1.3
+     */
+    public static <K, V> TimerTask checkMinIdle(final KeyedObjectPool<K, V> keyedPool, final K key, final int minIdle, final long period) throws IllegalArgumentException {
+        if (keyedPool == null) {
+            throw new IllegalArgumentException("keyedPool must not be null.");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("key must not be null.");
+        }
+        if (minIdle < 0) {
+            throw new IllegalArgumentException("minIdle must be non-negative.");
+        }
+        final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<K, V>(keyedPool, key, minIdle);
+        getMinIdleTimer().schedule(task, 0L, period);
+        return task;
+    }
+
+    /**
+     * Periodically check the idle object count for each key in the <code>Collection</code> <code>keys</code> in the keyedPool.
+     * At most one idle object will be added per period.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the keyedPool to check periodically.
+     * @param keys a collection of keys to check the idle object count.
+     * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
+     * @param period the frequency to check the number of idle objects in a keyedPool, see
+     *      {@link Timer#schedule(TimerTask, long, long)}.
+     * @return a {@link Map} of key and {@link TimerTask} pairs that will periodically check the pools idle object count.
+     * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or any of the values in the
+     *      collection is <code>null</code> or when <code>minIdle</code> is negative or when <code>period</code> isn't
+     *      valid for {@link Timer#schedule(TimerTask, long, long)}.
+     * @see #checkMinIdle(KeyedObjectPool, Object, int, long)
+     * @since Pool 1.3
+     */
+    public static <K, V> Map<K, TimerTask> checkMinIdle(final KeyedObjectPool<K, V> keyedPool, final Collection<? extends K> keys, final int minIdle, final long period) throws IllegalArgumentException {
+        if (keys == null) {
+            throw new IllegalArgumentException("keys must not be null.");
+        }
+        final Map<K, TimerTask> tasks = new HashMap<K, TimerTask>(keys.size());
+        final Iterator<? extends K> iter = keys.iterator();
+        while (iter.hasNext()) {
+            final K key = iter.next();
+            final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
+            tasks.put(key, task);
+        }
+        return tasks;
+    }
+
+    /**
+     * Call <code>addObject()</code> on <code>pool</code> <code>count</code> number of times.
+     *
+     * @param <T> the type of object
+     * @param pool the pool to prefill.
+     * @param count the number of idle objects to add.
+     * @throws Exception when {@link ObjectPool#addObject()} fails.
+     * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
+     * @since Pool 1.3
+     */
+    public static <T> void prefill(final ObjectPool<T> pool, final int count) throws Exception, IllegalArgumentException {
+        if (pool == null) {
+            throw new IllegalArgumentException("pool must not be null.");
+        }
+        for (int i = 0; i < count; i++) {
+            pool.addObject();
+        }
+    }
+
+    /**
+     * Call <code>addObject(Object)</code> on <code>keyedPool</code> with <code>key</code> <code>count</code>
+     * number of times.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the keyedPool to prefill.
+     * @param key the key to add objects for.
+     * @param count the number of idle objects to add for <code>key</code>.
+     * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
+     * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
+     * @since Pool 1.3
+     */
+    public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, final K key, final int count) throws Exception, IllegalArgumentException {
+        if (keyedPool == null) {
+            throw new IllegalArgumentException("keyedPool must not be null.");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("key must not be null.");
+        }
+        for (int i = 0; i < count; i++) {
+            keyedPool.addObject(key);
+        }
+    }
+
+    /**
+     * Call <code>addObject(Object)</code> on <code>keyedPool</code> with each key in <code>keys</code> for
+     * <code>count</code> number of times. This has the same effect as calling
+     * {@link #prefill(KeyedObjectPool, Object, int)} for each key in the <code>keys</code> collection.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the keyedPool to prefill.
+     * @param keys {@link Collection} of keys to add objects for.
+     * @param count the number of idle objects to add for each <code>key</code>.
+     * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
+     * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or
+     *      any value in <code>keys</code> is <code>null</code>.
+     * @see #prefill(KeyedObjectPool, Object, int)
+     * @since Pool 1.3
+     */
+    public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, final Collection<? extends K> keys, final int count) throws Exception, IllegalArgumentException {
+        if (keys == null) {
+            throw new IllegalArgumentException("keys must not be null.");
+        }
+        final Iterator<? extends K> iter = keys.iterator();
+        while (iter.hasNext()) {
+            prefill(keyedPool, iter.next(), count);
+        }
+    }
+
+    /**
+     * Returns a synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
+     *
+     * <p><b>Note:</b>
+     * This should not be used on pool implementations that already provide proper synchronization
+     * such as the pools provided in the Commons Pool library. Wrapping a pool that
+     * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
+     * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
+     * </p>
+     *
+     * @param <T> the type of object
+     * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
+     * @return a synchronized view of the specified ObjectPool.
+     * @since Pool 1.3
+     */
+    public static <T> ObjectPool<T> synchronizedPool(final ObjectPool<T> pool) {
+        if (pool == null) {
+            throw new IllegalArgumentException("pool must not be null.");
+        }
+        /*
+        assert !(pool instanceof GenericObjectPool)
+                : "GenericObjectPool is already thread-safe";
+        assert !(pool instanceof SoftReferenceObjectPool)
+                : "SoftReferenceObjectPool is already thread-safe";
+        assert !(pool instanceof StackObjectPool)
+                : "StackObjectPool is already thread-safe";
+        assert !"org.apache.commons.pool.composite.CompositeObjectPool".equals(pool.getClass().getName())
+                : "CompositeObjectPools are already thread-safe";
+        */
+        return new SynchronizedObjectPool<T>(pool);
+    }
+
+    /**
+     * Returns a synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
+     *
+     * <p><b>Note:</b>
+     * This should not be used on pool implementations that already provide proper synchronization
+     * such as the pools provided in the Commons Pool library. Wrapping a pool that
+     * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
+     * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
+     * </p>
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the KeyedObjectPool to be "wrapped" in a synchronized KeyedObjectPool.
+     * @return a synchronized view of the specified KeyedObjectPool.
+     * @since Pool 1.3
+     */
+    public static <K, V> KeyedObjectPool<K, V> synchronizedPool(final KeyedObjectPool<K, V> keyedPool) {
+        if (keyedPool == null) {
+            throw new IllegalArgumentException("keyedPool must not be null.");
+        }
+        /*
+        assert !(keyedPool instanceof GenericKeyedObjectPool)
+                : "GenericKeyedObjectPool is already thread-safe";
+        assert !(keyedPool instanceof StackKeyedObjectPool)
+                : "StackKeyedObjectPool is already thread-safe";
+        assert !"org.apache.commons.pool.composite.CompositeKeyedObjectPool".equals(keyedPool.getClass().getName())
+                : "CompositeKeyedObjectPools are already thread-safe";
+        */
+        return new SynchronizedKeyedObjectPool<K, V>(keyedPool);
+    }
+
+    /**
+     * Returns a synchronized (thread-safe) PoolableObjectFactory backed by the specified PoolableObjectFactory.
+     *
+     * @param <T> the type of object
+     * @param factory the PoolableObjectFactory to be "wrapped" in a synchronized PoolableObjectFactory.
+     * @return a synchronized view of the specified PoolableObjectFactory.
+     * @since Pool 1.3
+     */
+    public static <T> PoolableObjectFactory<T> synchronizedPoolableFactory(final PoolableObjectFactory<T> factory) {
+        return new SynchronizedPoolableObjectFactory<T>(factory);
+    }
+
+    /**
+     * Returns a synchronized (thread-safe) KeyedPoolableObjectFactory backed by the specified KeyedPoolableObjectFactory.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedFactory the KeyedPoolableObjectFactory to be "wrapped" in a synchronized KeyedPoolableObjectFactory.
+     * @return a synchronized view of the specified KeyedPoolableObjectFactory.
+     * @since Pool 1.3
+     */
+    public static <K, V> KeyedPoolableObjectFactory<K, V> synchronizedPoolableFactory(final KeyedPoolableObjectFactory<K, V> keyedFactory) {
+        return new SynchronizedKeyedPoolableObjectFactory<K, V>(keyedFactory);
+    }
+
+    /**
+     * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * This is intended as an always thread-safe alternative to using an idle object evictor
+     * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
+     * pools that experience load spikes.
+     *
+     * @param <T> the type of object
+     * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
+     * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * @see #erodingPool(ObjectPool, float)
+     * @since Pool 1.4
+     */
+    public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool) {
+        return erodingPool(pool, 1f);
+    }
+
+    /**
+     * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * This is intended as an always thread-safe alternative to using an idle object evictor
+     * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
+     * pools that experience load spikes.
+     *
+     * <p>
+     * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
+     * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
+     * Values greater than 1 cause the pool to less frequently try to shrink it's size.
+     * </p>
+     *
+     * @param <T> the type of object
+     * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
+     * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
+     * If 0 < factor < 1 then the pool shrinks more aggressively.
+     * If 1 < factor then the pool shrinks less aggressively.
+     * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * @see #erodingPool(ObjectPool)
+     * @since Pool 1.4
+     */
+    public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool, final float factor) {
+        if (pool == null) {
+            throw new IllegalArgumentException("pool must not be null.");
+        }
+        if (factor <= 0f) {
+            throw new IllegalArgumentException("factor must be positive.");
+        }
+        return new ErodingObjectPool<T>(pool, factor);
+    }
+
+    /**
+     * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * This is intended as an always thread-safe alternative to using an idle object evictor
+     * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
+     * pools that experience load spikes.
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
+     * possible.
+     * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * @see #erodingPool(KeyedObjectPool, float)
+     * @see #erodingPool(KeyedObjectPool, float, boolean)
+     * @since Pool 1.4
+     */
+    public static <K, V> KeyedObjectPool<K, V> erodingPool(final KeyedObjectPool<K, V> keyedPool) {
+        return erodingPool(keyedPool, 1f);
+    }
+
+    /**
+     * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * This is intended as an always thread-safe alternative to using an idle object evictor
+     * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
+     * pools that experience load spikes.
+     *
+     * <p>
+     * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
+     * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
+     * Values greater than 1 cause the pool to less frequently try to shrink it's size.
+     * </p>
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
+     * possible.
+     * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
+     * If 0 < factor < 1 then the pool shrinks more aggressively.
+     * If 1 < factor then the pool shrinks less aggressively.
+     * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * @see #erodingPool(KeyedObjectPool, float, boolean)
+     * @since Pool 1.4
+     */
+    public static <K, V> KeyedObjectPool<K, V> erodingPool(final KeyedObjectPool<K, V> keyedPool, final float factor) {
+        return erodingPool(keyedPool, factor, false);
+    }
+
+    /**
+     * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * This is intended as an always thread-safe alternative to using an idle object evictor
+     * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
+     * pools that experience load spikes.
+     *
+     * <p>
+     * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
+     * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
+     * Values greater than 1 cause the pool to less frequently try to shrink it's size.
+     * </p>
+     *
+     * <p>
+     * The perKey parameter determines if the pool shrinks on a whole pool basis or a per key basis.
+     * When perKey is false, the keys do not have an effect on the rate at which the pool tries to
+     * shrink it's size. When perKey is true, each key is shrunk independently.
+     * </p>
+     *
+     * @param <K> the type of key
+     * @param <V> the type of object
+     * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
+     * possible.
+     * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
+     * If 0 < factor < 1 then the pool shrinks more aggressively.
+     * If 1 < factor then the pool shrinks less aggressively.
+     * @param perKey when true, each key is treated independently.
+     * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
+     * @see #erodingPool(KeyedObjectPool)
+     * @see #erodingPool(KeyedObjectPool, float)
+     * @since Pool 1.4
+     */
+    public static <K, V> KeyedObjectPool<K, V> erodingPool(final KeyedObjectPool<K, V> keyedPool, final float factor, final boolean perKey) {
+        if (keyedPool == null) {
+            throw new IllegalArgumentException("keyedPool must not be null.");
+        }
+        if (factor <= 0f) {
+            throw new IllegalArgumentException("factor must be positive.");
+        }
+        if (perKey) {
+            return new ErodingPerKeyKeyedObjectPool<K, V>(keyedPool, factor);
+        } else {
+            return new ErodingKeyedObjectPool<K, V>(keyedPool, factor);
+        }
+    }
+
+    /**
+     * Get the <code>Timer</code> for checking keyedPool's idle count. Lazily create the {@link Timer} as needed.
+     *
+     * @return the {@link Timer} for checking keyedPool's idle count.
+     * @since Pool 1.3
+     */
+    private static synchronized Timer getMinIdleTimer() {
+        if (MIN_IDLE_TIMER == null) {
+            MIN_IDLE_TIMER = new Timer(true);
+        }
+        return MIN_IDLE_TIMER;
+    }
+
+    /**
+     * Adaptor class that wraps and converts a KeyedPoolableObjectFactory with a fixed
+     * key to a PoolableObjectFactory.
+     */
+    private static class PoolableObjectFactoryAdaptor<K, V> implements PoolableObjectFactory<V> {
+        /** Fixed key */
+        private final K key;
+        
+        /** Wrapped factory */
+        private final KeyedPoolableObjectFactory<K, V> keyedFactory;
+
+        /**
+         * Create a PoolableObjectFactoryAdaptor wrapping the provided KeyedPoolableObjectFactory with the 
+         * given fixed key.
+         * 
+         * @param keyedFactory KeyedPoolableObjectFactory that will manage objects
+         * @param key fixed key
+         * @throws IllegalArgumentException if either of the parameters is null
+         */
+        PoolableObjectFactoryAdaptor(final KeyedPoolableObjectFactory<K, V> keyedFactory, final K key)
+        throws IllegalArgumentException {
+            if (keyedFactory == null) {
+                throw new IllegalArgumentException("keyedFactory must not be null.");
+            }
+            if (key == null) {
+                throw new IllegalArgumentException("key must not be null.");
+            }
+            this.keyedFactory = keyedFactory;
+            this.key = key;
+        }
+
+        /**
+         * Create an object instance using the configured factory and key.
+         * 
+         * @return new object instance
+         */
+        public V makeObject() throws Exception {
+            return keyedFactory.makeObject(key);
+        }
+
+        /**
+         * Destroy the object, passing the fixed key to the factory.
+         * 
+         * @param obj object to destroy
+         */
+        public void destroyObject(final V obj) throws Exception {
+            keyedFactory.destroyObject(key, obj);
+        }
+
+        /**
+         * Validate the object, passing the fixed key to the factory.
+         * 
+         * @param obj object to validate
+         * @return true if validation is successful
+         */
+        public boolean validateObject(final V obj) {
+            return keyedFactory.validateObject(key, obj);
+        }
+
+        /**
+         * Activate the object, passing the fixed key to the factory.
+         * 
+         * @param obj object to activate
+         */
+        public void activateObject(final V obj) throws Exception {
+            keyedFactory.activateObject(key, obj);
+        }
+
+        /**
+         * Passivate the object, passing the fixed key to the factory.
+         * 
+         * @param obj object to passivate
+         */
+        public void passivateObject(final V obj) throws Exception {
+            keyedFactory.passivateObject(key, obj);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("PoolableObjectFactoryAdaptor");
+            sb.append("{key=").append(key);
+            sb.append(", keyedFactory=").append(keyedFactory);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Adaptor class that turns a PoolableObjectFactory into a KeyedPoolableObjectFactory by
+     * ignoring keys.
+     */
+    private static class KeyedPoolableObjectFactoryAdaptor<K, V> implements KeyedPoolableObjectFactory<K, V> {
+        
+        /** Underlying PoolableObjectFactory */
+        private final PoolableObjectFactory<V> factory;
+
+        /**
+         * Create a new KeyedPoolableObjectFactoryAdaptor using the given PoolableObjectFactory to
+         * manage objects.
+         * 
+         * @param factory wrapped PoolableObjectFactory 
+         * @throws IllegalArgumentException if the factory is null
+         */
+        KeyedPoolableObjectFactoryAdaptor(final PoolableObjectFactory<V> factory) throws IllegalArgumentException {
+            if (factory == null) {
+                throw new IllegalArgumentException("factory must not be null.");
+            }
+            this.factory = factory;
+        }
+
+        /**
+         * Create a new object instance, ignoring the key
+         * 
+         * @param key ignored
+         * @return newly created object instance
+         */
+        public V makeObject(final K key) throws Exception {
+            return factory.makeObject();
+        }
+
+        /**
+         * Destroy the object, ignoring the key.
+         * 
+         * @param key ignored
+         * @param obj instance to destroy
+         */
+        public void destroyObject(final K key, final V obj) throws Exception {
+            factory.destroyObject(obj);
+        }
+
+        /**
+         * Validate the object, ignoring the key
+         * 
+         * @param key ignored
+         * @param obj object to validate
+         * @return true if validation is successful
+         */
+        public boolean validateObject(final K key, final V obj) {
+            return factory.validateObject(obj);
+        }
+
+        /**
+         * Activate the object, ignoring the key.
+         * 
+         * @param key ignored
+         * @param obj object to be activated
+         */
+        public void activateObject(final K key, final V obj) throws Exception {
+            factory.activateObject(obj);
+        }
+
+        /**
+         * Passivate the object, ignoring the key.
+         * 
+         * @param key ignored
+         * @param obj object to passivate
+         */
+        public void passivateObject(final K key, final V obj) throws Exception {
+            factory.passivateObject(obj);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("KeyedPoolableObjectFactoryAdaptor");
+            sb.append("{factory=").append(factory);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Adapts a KeyedObjectPool to make it an ObjectPool by fixing restricting to
+     * a fixed key.
+     */
+    private static class ObjectPoolAdaptor<V> implements ObjectPool<V> {
+        
+        /** Fixed key */
+        private final Object key;
+        
+        /** Underlying KeyedObjectPool */
+        private final KeyedObjectPool<Object, V> keyedPool;
+
+        /**
+         * Create a new ObjectPoolAdaptor using the provided KeyedObjectPool and fixed key.
+         * 
+         * @param keyedPool underlying KeyedObjectPool
+         * @param key fixed key
+         * @throws IllegalArgumentException if either of the parameters is null
+         */
+        ObjectPoolAdaptor(final KeyedObjectPool<Object, V> keyedPool, final Object key) throws IllegalArgumentException {
+            if (keyedPool == null) {
+                throw new IllegalArgumentException("keyedPool must not be null.");
+            }
+            if (key == null) {
+                throw new IllegalArgumentException("key must not be null.");
+            }
+            this.keyedPool = keyedPool;
+            this.key = key;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
+            return keyedPool.borrowObject(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void returnObject(final V obj) {
+            try {
+                keyedPool.returnObject(key, obj);
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void invalidateObject(final V obj) {
+            try {
+                keyedPool.invalidateObject(key, obj);
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject() throws Exception, IllegalStateException {
+            keyedPool.addObject(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            return keyedPool.getNumIdle(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            return keyedPool.getNumActive(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            keyedPool.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                keyedPool.close();
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Sets the PoolableObjectFactory for the pool.
+         * 
+         * @param factory new PoolableObjectFactory 
+         * @deprecated to be removed in version 2.0
+         */
+        @Deprecated
+        public void setFactory(final PoolableObjectFactory<V> factory) throws IllegalStateException, UnsupportedOperationException {
+            keyedPool.setFactory(adapt(factory));
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("ObjectPoolAdaptor");
+            sb.append("{key=").append(key);
+            sb.append(", keyedPool=").append(keyedPool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Adapts an ObjectPool to implement KeyedObjectPool by ignoring key arguments.
+     */
+    private static class KeyedObjectPoolAdaptor<K, V> implements KeyedObjectPool<K, V> {
+       
+        /** Underlying pool */
+        private final ObjectPool<V> pool;
+
+        /**
+         * Create a new KeyedObjectPoolAdaptor wrapping the given ObjectPool
+         * 
+         * @param pool underlying object pool
+         * @throws IllegalArgumentException if pool is null
+         */
+        KeyedObjectPoolAdaptor(final ObjectPool<V> pool) throws IllegalArgumentException {
+            if (pool == null) {
+                throw new IllegalArgumentException("pool must not be null.");
+            }
+            this.pool = pool;
+        }
+
+        /**
+         * Borrow and object from the pool, ignoring the key
+         * 
+         * @param key ignored
+         * @return newly created object instance
+         */
+        public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException {
+            return pool.borrowObject();
+        }
+
+        /**
+         * Return and object to the pool, ignoring the key
+         * 
+         * @param key ignored
+         * @param obj object to return
+         */
+        public void returnObject(final K key, final V obj) {
+            try {
+                pool.returnObject(obj);
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Invalidate and object, ignoring the key
+         * 
+         * @param obj object to invalidate
+         * @param key ignored
+         */
+        public void invalidateObject(final K key, final V obj) {
+            try {
+                pool.invalidateObject(obj);
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Add an object to the pool, ignoring the key
+         * 
+         * @param key ignored
+         */
+        public void addObject(final K key) throws Exception, IllegalStateException {
+            pool.addObject();
+        }
+
+        /**
+         * Return the number of objects idle in the pool, ignoring the key.
+         * 
+         * @param key ignored
+         * @return idle instance count
+         */
+        public int getNumIdle(final K key) throws UnsupportedOperationException {
+            return pool.getNumIdle();
+        }
+
+        /**
+         * Return the number of objects checked out from the pool, ignoring the key.
+         * 
+         * @param key ignored
+         * @return active instance count
+         */
+        public int getNumActive(final K key) throws UnsupportedOperationException {
+            return pool.getNumActive();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            return pool.getNumIdle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            return pool.getNumActive();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            pool.clear();
+        }
+
+        /**
+         * Clear the pool, ignoring the key (has same effect as {@link #clear()}.
+         * 
+         * @param key ignored.
+         */
+        public void clear(final K key) throws Exception, UnsupportedOperationException {
+            pool.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                pool.close();
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Sets the factory used to manage objects.
+         * 
+         * @param factory new factory to use managing object instances
+         * @deprecated to be removed in version 2.0
+         */
+        @Deprecated
+        @SuppressWarnings("unchecked")
+        public void setFactory(final KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException, UnsupportedOperationException {
+            pool.setFactory(adapt((KeyedPoolableObjectFactory<Object, V>)factory));
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("KeyedObjectPoolAdaptor");
+            sb.append("{pool=").append(pool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * An object pool that performs type checking on objects passed
+     * to pool methods.
+     *
+     */
+    private static class CheckedObjectPool<T> implements ObjectPool<T> {
+        /** 
+         * Type of objects allowed in the pool. This should be a subtype of the return type of
+         * the underlying pool's associated object factory.
+         */
+        private final Class<T> type;
+       
+        /** Underlying object pool */
+        private final ObjectPool<T> pool;
+
+        /**
+         * Create a CheckedObjectPool accepting objects of the given type using
+         * the given pool.
+         * 
+         * @param pool underlying object pool
+         * @param type expected pooled object type
+         * @throws IllegalArgumentException if either parameter is null
+         */
+        CheckedObjectPool(final ObjectPool<T> pool, final Class<T> type) {
+            if (pool == null) {
+                throw new IllegalArgumentException("pool must not be null.");
+            }
+            if (type == null) {
+                throw new IllegalArgumentException("type must not be null.");
+            }
+            this.pool = pool;
+            this.type = type;
+        }
+
+        /**
+         * Borrow an object from the pool, checking its type.
+         * 
+         * @return a type-checked object from the pool
+         * @throws ClassCastException if the object returned by the pool is not of the expected type
+         */
+        public T borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
+            final T obj = pool.borrowObject();
+            if (type.isInstance(obj)) {
+                return obj;
+            } else {
+                throw new ClassCastException("Borrowed object is not of type: " + type.getName() + " was: " + obj);
+            }
+        }
+
+        /**
+         * Return an object to the pool, verifying that it is of the correct type.
+         * 
+         * @param obj object to return
+         * @throws ClassCastException if obj is not of the expected type
+         */
+        public void returnObject(final T obj) {
+            if (type.isInstance(obj)) {
+                try {
+                    pool.returnObject(obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            } else {
+                throw new ClassCastException("Returned object is not of type: " + type.getName() + " was: " + obj);
+            }
+        }
+
+        /**
+         * Invalidates an object from the pool, verifying that it is of the expected type.
+         * 
+         * @param obj object to invalidate
+         * @throws ClassCastException if obj is not of the expected type
+         */
+        public void invalidateObject(final T obj) {
+            if (type.isInstance(obj)) {
+                try {
+                    pool.invalidateObject(obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            } else {
+                throw new ClassCastException("Invalidated object is not of type: " + type.getName() + " was: " + obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
+            pool.addObject();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            return pool.getNumIdle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            return pool.getNumActive();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            pool.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                pool.close();
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Sets the object factory associated with the pool
+         * 
+         * @param factory object factory
+         * @deprecated to be removed in version 2.0
+         */
+        @Deprecated
+        public void setFactory(final PoolableObjectFactory<T> factory) throws IllegalStateException, UnsupportedOperationException {
+            pool.setFactory(factory);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("CheckedObjectPool");
+            sb.append("{type=").append(type);
+            sb.append(", pool=").append(pool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * A keyed object pool that performs type checking on objects passed
+     * to pool methods.
+     *
+     */
+    private static class CheckedKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
+        /** 
+         * Expected type of objects managed by the pool.  This should be
+         * a subtype of the return type of the object factory used by the pool.
+         */
+        private final Class<V> type;
+        
+        /** Underlying pool */
+        private final KeyedObjectPool<K, V> keyedPool;
+
+        /**
+         * Create a new CheckedKeyedObjectPool from the given pool with given expected object type.
+         * 
+         * @param keyedPool underlying pool
+         * @param type expected object type
+         * @throws IllegalArgumentException if either parameter is null
+         */
+        CheckedKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, final Class<V> type) {
+            if (keyedPool == null) {
+                throw new IllegalArgumentException("keyedPool must not be null.");
+            }
+            if (type == null) {
+                throw new IllegalArgumentException("type must not be null.");
+            }
+            this.keyedPool = keyedPool;
+            this.type = type;
+        }
+
+        /**
+         * Borrow an object from the pool, verifying correct return type.
+         * 
+         * @param key pool key
+         * @return type-checked object from the pool under the given key
+         * @throws ClassCastException if the object returned by the pool is not of the expected type
+         */
+        public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException {
+            V obj = keyedPool.borrowObject(key);
+            if (type.isInstance(obj)) {
+                return obj;
+            } else {
+                throw new ClassCastException("Borrowed object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
+            }
+        }
+
+        /**
+         * Return an object to the pool, checking its type.
+         * 
+         * @param key the associated key (not type-checked)
+         * @param obj the object to return (type-checked)
+         * @throws ClassCastException if obj is not of the expected type
+         */
+        public void returnObject(final K key, final V obj) {
+            if (type.isInstance(obj)) {
+                try {
+                    keyedPool.returnObject(key, obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            } else {
+                throw new ClassCastException("Returned object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
+            }
+        }
+
+        /**
+         * Invalidate an object to the pool, checking its type.
+         * 
+         * @param key the associated key (not type-checked)
+         * @param obj the object to return (type-checked)
+         * @throws ClassCastException if obj is not of the expected type
+         */
+        public void invalidateObject(final K key, final V obj) {
+            if (type.isInstance(obj)) {
+                try {
+                    keyedPool.invalidateObject(key, obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            } else {
+                throw new ClassCastException("Invalidated object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject(final K key) throws Exception, IllegalStateException, UnsupportedOperationException {
+            keyedPool.addObject(key);
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle(final K key) throws UnsupportedOperationException {
+            return keyedPool.getNumIdle(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive(final K key) throws UnsupportedOperationException {
+            return keyedPool.getNumActive(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            return keyedPool.getNumIdle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            return keyedPool.getNumActive();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            keyedPool.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear(final K key) throws Exception, UnsupportedOperationException {
+            keyedPool.clear(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                keyedPool.close();
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Sets the object factory associated with the pool
+         * 
+         * @param factory object factory
+         * @deprecated to be removed in version 2.0
+         */
+        @Deprecated
+        public void setFactory(final KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException, UnsupportedOperationException {
+            keyedPool.setFactory(factory);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("CheckedKeyedObjectPool");
+            sb.append("{type=").append(type);
+            sb.append(", keyedPool=").append(keyedPool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Timer task that adds objects to the pool until the number of idle
+     * instances reaches the configured minIdle.  Note that this is not the
+     * same as the pool's minIdle setting.
+     * 
+     */
+    private static class ObjectPoolMinIdleTimerTask<T> extends TimerTask {
+        
+        /** Minimum number of idle instances.  Not the same as pool.getMinIdle(). */
+        private final int minIdle;
+        
+        /** Object pool */
+        private final ObjectPool<T> pool;
+
+        /**
+         * Create a new ObjectPoolMinIdleTimerTask for the given pool with the given minIdle setting.
+         * 
+         * @param pool object pool
+         * @param minIdle number of idle instances to maintain
+         * @throws IllegalArgumentException if the pool is null
+         */
+        ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle) throws IllegalArgumentException {
+            if (pool == null) {
+                throw new IllegalArgumentException("pool must not be null.");
+            }
+            this.pool = pool;
+            this.minIdle = minIdle;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void run() {
+            boolean success = false;
+            try {
+                if (pool.getNumIdle() < minIdle) {
+                    pool.addObject();
+                }
+                success = true;
+
+            } catch (Exception e) {
+                cancel();
+
+            } finally {
+                // detect other types of Throwable and cancel this Timer
+                if (!success) {
+                    cancel();
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("ObjectPoolMinIdleTimerTask");
+            sb.append("{minIdle=").append(minIdle);
+            sb.append(", pool=").append(pool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Timer task that adds objects to the pool until the number of idle
+     * instances for the given key reaches the configured minIdle.  Note that this is not the
+     * same as the pool's minIdle setting.
+     * 
+     */
+    private static class KeyedObjectPoolMinIdleTimerTask<K, V> extends TimerTask {
+        /** Minimum number of idle instances.  Not the same as pool.getMinIdle(). */
+        private final int minIdle;
+        
+        /** Key to ensure minIdle for */
+        private final K key;
+        
+        /** Keyed object pool */
+        private final KeyedObjectPool<K, V> keyedPool;
+
+        /**
+         * Create a new KeyedObjecPoolMinIdleTimerTask.
+         * 
+         * @param keyedPool keyed object pool
+         * @param key key to ensure minimum number of idle instances
+         * @param minIdle minimum number of idle instances 
+         * @throws IllegalArgumentException if the key is null
+         */
+        KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool<K, V> keyedPool, final K key, final int minIdle) throws IllegalArgumentException {
+            if (keyedPool == null) {
+                throw new IllegalArgumentException("keyedPool must not be null.");
+            }
+            this.keyedPool = keyedPool;
+            this.key = key;
+            this.minIdle = minIdle;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void run() {
+            boolean success = false;
+            try {
+                if (keyedPool.getNumIdle(key) < minIdle) {
+                    keyedPool.addObject(key);
+                }
+                success = true;
+
+            } catch (Exception e) {
+                cancel();
+
+            } finally {
+                // detect other types of Throwable and cancel this Timer
+                if (!success) {
+                    cancel();
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("KeyedObjectPoolMinIdleTimerTask");
+            sb.append("{minIdle=").append(minIdle);
+            sb.append(", key=").append(key);
+            sb.append(", keyedPool=").append(keyedPool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * A synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
+     *
+     * <p><b>Note:</b>
+     * This should not be used on pool implementations that already provide proper synchronization
+     * such as the pools provided in the Commons Pool library. Wrapping a pool that
+     * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
+     * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
+     * </p>
+     */
+    private static class SynchronizedObjectPool<T> implements ObjectPool<T> {
+        
+        /** Object whose monitor is used to synchronize methods on the wrapped pool. */
+        private final Object lock;
+        
+        /** the underlying object pool */
+        private final ObjectPool<T> pool;
+
+        /**
+         * Create a new SynchronizedObjectPool wrapping the given pool.
+         * 
+         * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
+         * @throws IllegalArgumentException if the pool is null
+         */
+        SynchronizedObjectPool(final ObjectPool<T> pool) throws IllegalArgumentException {
+            if (pool == null) {
+                throw new IllegalArgumentException("pool must not be null.");
+            }
+            this.pool = pool;
+            lock = new Object();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public T borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
+            synchronized (lock) {
+                return pool.borrowObject();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void returnObject(final T obj) {
+            synchronized (lock) {
+                try {
+                    pool.returnObject(obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void invalidateObject(final T obj) {
+            synchronized (lock) {
+                try {
+                    pool.invalidateObject(obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
+            synchronized (lock) {
+                pool.addObject();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            synchronized (lock) {
+                return pool.getNumIdle();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            synchronized (lock) {
+                return pool.getNumActive();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            synchronized (lock) {
+                pool.clear();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                synchronized (lock) {
+                    pool.close();
+                }
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Sets the factory used by the pool.
+         * 
+         * @param factory new PoolableObjectFactory
+         * @deprecated to be removed in pool 2.0
+         */
+        @Deprecated
+        public void setFactory(final PoolableObjectFactory<T> factory) throws IllegalStateException, UnsupportedOperationException {
+            synchronized (lock) {
+                pool.setFactory(factory);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("SynchronizedObjectPool");
+            sb.append("{pool=").append(pool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * A synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
+     *
+     * <p><b>Note:</b>
+     * This should not be used on pool implementations that already provide proper synchronization
+     * such as the pools provided in the Commons Pool library. Wrapping a pool that
+     * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
+     * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
+     * </p>
+     */
+    private static class SynchronizedKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
+        
+        /** Object whose monitor is used to synchronize methods on the wrapped pool. */
+        private final Object lock;
+        
+        /** Underlying object pool */
+        private final KeyedObjectPool<K, V> keyedPool;
+
+        /**
+         * Create a new SynchronizedKeyedObjectPool wrapping the given pool
+         * 
+         * @param keyedPool KeyedObjectPool to wrap
+         * @throws IllegalArgumentException if keyedPool is null
+         */
+        SynchronizedKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool) throws IllegalArgumentException {
+            if (keyedPool == null) {
+                throw new IllegalArgumentException("keyedPool must not be null.");
+            }
+            this.keyedPool = keyedPool;
+            lock = new Object();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException {
+            synchronized (lock) {
+                return keyedPool.borrowObject(key);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void returnObject(final K key, final V obj) {
+            synchronized (lock) {
+                try {
+                    keyedPool.returnObject(key, obj);
+                } catch (Exception e) {
+                    // swallowed
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void invalidateObject(final K key, final V obj) {
+            synchronized (lock) {
+                try {
+                    keyedPool.invalidateObject(key, obj);
+                } catch (Exception e) {
+                    // swallowed as of Pool 2
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject(final K key) throws Exception, IllegalStateException, UnsupportedOperationException {
+            synchronized (lock) {
+                keyedPool.addObject(key);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle(final K key) throws UnsupportedOperationException {
+            synchronized (lock) {
+                return keyedPool.getNumIdle(key);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive(final K key) throws UnsupportedOperationException {
+            synchronized (lock) {
+                return keyedPool.getNumActive(key);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            synchronized (lock) {
+                return keyedPool.getNumIdle();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            synchronized (lock) {
+                return keyedPool.getNumActive();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            synchronized (lock) {
+                keyedPool.clear();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear(final K key) throws Exception, UnsupportedOperationException {
+            synchronized (lock) {
+                keyedPool.clear(key);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                synchronized (lock) {
+                    keyedPool.close();
+                }
+            } catch (Exception e) {
+                // swallowed as of Pool 2
+            }
+        }
+
+        /**
+         * Sets the object factory used by the pool.
+         * 
+         * @param factory KeyedPoolableObjectFactory used by the pool
+         * @deprecated to be removed in pool 2.0
+         */
+        @Deprecated
+        public void setFactory(final KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException, UnsupportedOperationException {
+            synchronized (lock) {
+                keyedPool.setFactory(factory);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("SynchronizedKeyedObjectPool");
+            sb.append("{keyedPool=").append(keyedPool);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * A fully synchronized PoolableObjectFactory that wraps a PoolableObjectFactory and synchronizes
+     * access to the wrapped factory methods.
+     *
+     * <p><b>Note:</b>
+     * This should not be used on pool implementations that already provide proper synchronization
+     * such as the pools provided in the Commons Pool library. </p>
+     */
+    private static class SynchronizedPoolableObjectFactory<T> implements PoolableObjectFactory<T> {
+        /** Synchronization lock */
+        private final Object lock;
+        
+        /** Wrapped factory */
+        private final PoolableObjectFactory<T> factory;
+
+        /** 
+         * Create a SynchronizedPoolableObjectFactory wrapping the given factory.
+         * 
+         * @param factory underlying factory to wrap
+         * @throws IllegalArgumentException if the factory is null
+         */
+        SynchronizedPoolableObjectFactory(final PoolableObjectFactory<T> factory) throws IllegalArgumentException {
+            if (factory == null) {
+                throw new IllegalArgumentException("factory must not be null.");
+            }
+            this.factory = factory;
+            lock = new Object();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public T makeObject() throws Exception {
+            synchronized (lock) {
+                return factory.makeObject();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void destroyObject(final T obj) throws Exception {
+            synchronized (lock) {
+                factory.destroyObject(obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean validateObject(final T obj) {
+            synchronized (lock) {
+                return factory.validateObject(obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void activateObject(final T obj) throws Exception {
+            synchronized (lock) {
+                factory.activateObject(obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void passivateObject(final T obj) throws Exception {
+            synchronized (lock) {
+                factory.passivateObject(obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("SynchronizedPoolableObjectFactory");
+            sb.append("{factory=").append(factory);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * A fully synchronized KeyedPoolableObjectFactory that wraps a KeyedPoolableObjectFactory and synchronizes
+     * access to the wrapped factory methods.
+     *
+     * <p><b>Note:</b>
+     * This should not be used on pool implementations that already provide proper synchronization
+     * such as the pools provided in the Commons Pool library. </p>
+     */
+    private static class SynchronizedKeyedPoolableObjectFactory<K, V> implements KeyedPoolableObjectFactory<K, V> {
+        /** Synchronization lock */
+        private final Object lock;
+        
+        /** Wrapped factory */
+        private final KeyedPoolableObjectFactory<K, V> keyedFactory;
+
+        /** 
+         * Create a SynchronizedKeyedPoolableObjectFactory wrapping the given factory.
+         * 
+         * @param keyedFactory underlying factory to wrap
+         * @throws IllegalArgumentException if the factory is null
+         */
+        SynchronizedKeyedPoolableObjectFactory(final KeyedPoolableObjectFactory<K, V> keyedFactory) throws IllegalArgumentException {
+            if (keyedFactory == null) {
+                throw new IllegalArgumentException("keyedFactory must not be null.");
+            }
+            this.keyedFactory = keyedFactory;
+            lock = new Object();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V makeObject(final K key) throws Exception {
+            synchronized (lock) {
+                return keyedFactory.makeObject(key);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void destroyObject(final K key, final V obj) throws Exception {
+            synchronized (lock) {
+                keyedFactory.destroyObject(key, obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean validateObject(final K key, final V obj) {
+            synchronized (lock) {
+                return keyedFactory.validateObject(key, obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void activateObject(final K key, final V obj) throws Exception {
+            synchronized (lock) {
+                keyedFactory.activateObject(key, obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void passivateObject(final K key, final V obj) throws Exception {
+            synchronized (lock) {
+                keyedFactory.passivateObject(key, obj);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("SynchronizedKeyedPoolableObjectFactory");
+            sb.append("{keyedFactory=").append(keyedFactory);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Encapsulate the logic for when the next poolable object should be discarded.
+     * Each time update is called, the next time to shrink is recomputed, based on
+     * the float factor, number of idle instances in the pool and high water mark.
+     * Float factor is assumed to be between 0 and 1.  Values closer to 1 cause
+     * less frequent erosion events.  Erosion event timing also depends on numIdle.
+     * When this value is relatively high (close to previously established high water
+     * mark), erosion occurs more frequently.
+     */
+    private static class ErodingFactor {
+        /** Determines frequency of "erosion" events */
+        private final float factor;
+        
+        /** Time of next shrink event */
+        private transient volatile long nextShrink;
+        
+        /** High water mark - largest numIdle encountered */
+        private transient volatile int idleHighWaterMark;
+
+        /**
+         * Create a new ErodingFactor with the given erosion factor.
+         * 
+         * @param factor erosion factor
+         */
+        public ErodingFactor(final float factor) {
+            this.factor = factor;
+            nextShrink = System.currentTimeMillis() + (long)(900000 * factor); // now + 15 min * factor
+            idleHighWaterMark = 1;
+        }
+
+        /**
+         * Updates internal state based on numIdle and the current time.
+         * 
+         * @param numIdle number of idle elements in the pool
+         */
+        public void update(final int numIdle) {
+            update(System.currentTimeMillis(), numIdle);
+        }
+
+        /**
+         * Updates internal state using the supplied time and numIdle.
+         * 
+         * @param now current time
+         * @param numIdle number of idle elements in the pool
+         */
+        public void update(final long now, final int numIdle) {
+            final int idle = Math.max(0, numIdle);
+            idleHighWaterMark = Math.max(idle, idleHighWaterMark);
+            final float maxInterval = 15f;
+            final float minutes = maxInterval + ((1f-maxInterval)/idleHighWaterMark) * idle;
+            nextShrink = now + (long)(minutes * 60000f * factor);
+        }
+
+        /**
+         * Returns the time of the next erosion event.
+         * 
+         * @return next shrink time
+         */
+        public long getNextShrink() {
+            return nextShrink;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            return "ErodingFactor{" +
+                    "factor=" + factor +
+                    ", idleHighWaterMark=" + idleHighWaterMark +
+                    '}';
+        }
+    }
+
+    /**
+     * Decorates an object pool, adding "eroding" behavior.  Based on the
+     * configured {@link #factor erosion factor}, objects returning to the pool
+     * may be invalidated instead of being added to idle capacity.
+     *
+     */
+    private static class ErodingObjectPool<T> implements ObjectPool<T> {
+        /** Underlying object pool */
+        private final ObjectPool<T> pool;
+        
+        /** Erosion factor */
+        private final ErodingFactor factor;
+
+        /** 
+         * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
+         * 
+         * @param pool underlying pool
+         * @param factor erosion factor - determines the frequency of erosion events
+         * @see #factor
+         */
+        public ErodingObjectPool(final ObjectPool<T> pool, final float factor) {
+            this.pool = pool;
+            this.factor = new ErodingFactor(factor);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public T borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
+            return pool.borrowObject();
+        }
+
+        /**
+         * Returns obj to the pool, unless erosion is triggered, in which
+         * case obj is invalidated.  Erosion is triggered when there are idle instances in 
+         * the pool and more than the {@link #factor erosion factor}-determined time has elapsed
+         * since the last returnObject activation. 
+         * 
+         * @param obj object to return or invalidate
+         * @see #factor
+         */
+        public void returnObject(final T obj) {
+            boolean discard = false;
+            final long now = System.currentTimeMillis();
+            synchronized (pool) {
+                if (factor.getNextShrink() < now) { // XXX: Pool 3: move test out of sync block
+                    final int numIdle = pool.getNumIdle();
+                    if (numIdle > 0) {
+                        discard = true;
+                    }
+
+                    factor.update(now, numIdle);
+                }
+            }
+            try {
+                if (discard) {
+                    pool.invalidateObject(obj);
+                } else {
+                    pool.returnObject(obj);
+                }
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void invalidateObject(final T obj) {
+            try {
+                pool.invalidateObject(obj);
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
+            pool.addObject();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            return pool.getNumIdle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            return pool.getNumActive();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            pool.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                pool.close();
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * @deprecated to be removed in pool 2.0
+         */
+        @Deprecated
+        public void setFactory(final PoolableObjectFactory<T> factory) throws IllegalStateException, UnsupportedOperationException {
+            pool.setFactory(factory);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            return "ErodingObjectPool{" +
+                    "factor=" + factor +
+                    ", pool=" + pool +
+                    '}';
+        }
+    }
+
+    /**
+     * Decorates a keyed object pool, adding "eroding" behavior.  Based on the
+     * configured {@link #factor erosion factor}, objects returning to the pool
+     * may be invalidated instead of being added to idle capacity.
+     *
+     */
+    private static class ErodingKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
+        /** Underlying pool */
+        private final KeyedObjectPool<K, V> keyedPool;
+        
+        /** Erosion factor */
+        private final ErodingFactor erodingFactor;
+
+        /** 
+         * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
+         * 
+         * @param keyedPool underlying pool
+         * @param factor erosion factor - determines the frequency of erosion events
+         * @see #erodingFactor
+         */
+        public ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, final float factor) {
+            this(keyedPool, new ErodingFactor(factor));
+        }
+
+        /** 
+         * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
+         * 
+         * @param keyedPool underlying pool - must not be null
+         * @param erodingFactor erosion factor - determines the frequency of erosion events
+         * @see #factor
+         */
+        protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, final ErodingFactor erodingFactor) {
+            if (keyedPool == null) {
+                throw new IllegalArgumentException("keyedPool must not be null.");
+            }
+            this.keyedPool = keyedPool;
+            this.erodingFactor = erodingFactor;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public V borrowObject(final K key) throws Exception, NoSuchElementException, IllegalStateException {
+            return keyedPool.borrowObject(key);
+        }
+
+        /**
+         * Returns obj to the pool, unless erosion is triggered, in which
+         * case obj is invalidated.  Erosion is triggered when there are idle instances in 
+         * the pool associated with the given key and more than the configured {@link #erodingFactor erosion factor}
+         * time has elapsed since the last returnObject activation. 
+         * 
+         * @param obj object to return or invalidate
+         * @param key key
+         * @see #erodingFactor
+         */
+        public void returnObject(final K key, final V obj) throws Exception {
+            boolean discard = false;
+            final long now = System.currentTimeMillis();
+            final ErodingFactor factor = getErodingFactor(key);
+            synchronized (keyedPool) {
+                if (factor.getNextShrink() < now) {
+                    final int numIdle = numIdle(key);
+                    if (numIdle > 0) {
+                        discard = true;
+                    }
+
+                    factor.update(now, numIdle);
+                }
+            }
+            try {
+                if (discard) {
+                    keyedPool.invalidateObject(key, obj);
+                } else {
+                    keyedPool.returnObject(key, obj);
+                }
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
+
+        /**
+         * Returns the total number of instances currently idle in this pool (optional operation).
+         * Returns a negative value if this information is not available.
+         *
+         * @param key ignored
+         * @return the total number of instances currently idle in this pool or a negative value if unsupported
+         * @throws UnsupportedOperationException <strong>deprecated</strong>: when this implementation doesn't support the operation
+         */
+        protected int numIdle(final K key) {
+            return getKeyedPool().getNumIdle();
+        }
+
+        /**
+         * Returns the eroding factor for the given key
+         * @param key key
+         * @return eroding factor for the given keyed pool
+         */
+        protected ErodingFactor getErodingFactor(final K key) {
+            return erodingFactor;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void invalidateObject(final K key, final V obj) {
+            try {
+                keyedPool.invalidateObject(key, obj);
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void addObject(final K key) throws Exception, IllegalStateException, UnsupportedOperationException {
+            keyedPool.addObject(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle() throws UnsupportedOperationException {
+            return keyedPool.getNumIdle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumIdle(final K key) throws UnsupportedOperationException {
+            return keyedPool.getNumIdle(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive() throws UnsupportedOperationException {
+            return keyedPool.getNumActive();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int getNumActive(final K key) throws UnsupportedOperationException {
+            return keyedPool.getNumActive(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear() throws Exception, UnsupportedOperationException {
+            keyedPool.clear();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void clear(final K key) throws Exception, UnsupportedOperationException {
+            keyedPool.clear(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() {
+            try {
+                keyedPool.close();
+            } catch (Exception e) {
+                // swallowed
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         * @deprecated to be removed in pool 2.0
+         */
+        @Deprecated
+        public void setFactory(final KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException, UnsupportedOperationException {
+            keyedPool.setFactory(factory);
+        }
+
+        /**
+         * Returns the underlying pool
+         * 
+         * @return the keyed pool that this ErodingKeyedObjectPool wraps
+         */
+        protected KeyedObjectPool<K, V> getKeyedPool() {
+            return keyedPool;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            return "ErodingKeyedObjectPool{" +
+                    "erodingFactor=" + erodingFactor +
+                    ", keyedPool=" + keyedPool +
+                    '}';
+        }
+    }
+
+    /**
+     * Extends ErodingKeyedObjectPool to allow erosion to take place on a per-key
+     * basis.  Timing of erosion events is tracked separately for separate keyed pools.
+     */
+    private static class ErodingPerKeyKeyedObjectPool<K, V> extends ErodingKeyedObjectPool<K, V> {
+        /** Erosion factor - same for all pools */
+        private final float factor;
+        
+        /** Map of ErodingFactor instances keyed on pool keys */
+        private final Map<K, ErodingFactor> factors = Collections.synchronizedMap(new HashMap<K, ErodingFactor>());
+
+        /**
+         * Create a new ErordingPerKeyKeyedObjectPool decorating the given keyed pool with
+         * the specified erosion factor.
+         * @param keyedPool underlying keyed pool
+         * @param factor erosion factor
+         */
+        public ErodingPerKeyKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, final float factor) {
+            super(keyedPool, null);
+            this.factor = factor;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        protected int numIdle(final K key) {
+            return getKeyedPool().getNumIdle(key);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        protected ErodingFactor getErodingFactor(final K key) {
+            ErodingFactor factor = factors.get(key);
+            // this may result in two ErodingFactors being created for a key
+            // since they are small and cheap this is okay.
+            if (factor == null) {
+                factor = new ErodingFactor(this.factor);
+                factors.put(key, factor);
+            }
+            return factor;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public String toString() {
+            return "ErodingPerKeyKeyedObjectPool{" +
+                    "factor=" + factor +
+                    ", keyedPool=" + getKeyedPool() +
+                    '}';
+        }
+    }
+}
\ No newline at end of file
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..4de2e8a
--- /dev/null
+++ b/src/java/org/apache/commons/pool/PoolableObjectFactory.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * An interface defining life-cycle methods for
+ * instances to be served by an {@link ObjectPool}.
+ * <p>
+ * By contract, when an {@link ObjectPool}
+ * delegates to a {@link PoolableObjectFactory},
+ * <ol>
+ *  <li>
+ *   {@link #makeObject makeObject}
+ *   is called whenever a new instance is needed.
+ *  </li>
+ *  <li>
+ *   {@link #activateObject activateObject}
+ *   is invoked on every instance that has been
+ *   {@link #passivateObject passivated} before it is
+ *   {@link ObjectPool#borrowObject borrowed} from the pool.
+ *  </li>
+ *  <li>
+ *   {@link #validateObject validateObject}
+ *   is invoked on {@link #activateObject activated} instances to make sure
+ *   they can be {@link ObjectPool#borrowObject borrowed} from the pool.
+ *   <code>validateObject</code> <strong>may</strong> also be used to test an
+ *   instance being {@link ObjectPool#returnObject returned} to the pool
+ *   before it is {@link #passivateObject passivated}. It will only be invoked
+ *   on an activated instance.
+ *  </li>
+ *  <li>
+ *   {@link #passivateObject passivateObject}
+ *   is invoked on every instance when it is returned to the pool.
+ *  </li>
+ *  <li>
+ *   {@link #destroyObject destroyObject}
+ *   is invoked on every instance when it is being "dropped" from the
+ *   pool (whether due to the response from <code>validateObject</code>,
+ *   or for reasons specific to the pool implementation.) There is no
+ *   guarantee that the instance being destroyed will
+ *   be considered active, passive or in a generally consistent state.
+ *  </li>
+ * </ol>
+ * </p>
+ * <p>
+ * {@link PoolableObjectFactory} must be thread-safe. The only promise
+ * an {@link ObjectPool} makes is that the same instance of an object will not
+ * be passed to more than one method of a <code>PoolableObjectFactory</code>
+ * at a time.
+ * </p>
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @see ObjectPool
+ *
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222388 $ $Date: 2011-12-22 13:28:27 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public interface PoolableObjectFactory<T> {
+  /**
+   * Creates an instance that can be served by the pool.
+   * Instances returned from this method should be in the
+   * same state as if they had been
+   * {@link #activateObject activated}. They will not be
+   * activated before being served by the pool.
+   *
+   * @return an instance that can be served by the pool.
+   * @throws Exception if there is a problem creating a new instance,
+   *    this will be propagated to the code requesting an object.
+   */
+  T makeObject() throws Exception;
+
+  /**
+   * Destroys an instance no longer needed by the pool.
+   * <p>
+   * It is important for implementations of this method to be aware
+   * that there is no guarantee about what state <code>obj</code>
+   * will be in and the implementation should be prepared to handle
+   * unexpected errors.
+   * </p>
+   * <p>
+   * Also, an implementation must take in to consideration that
+   * instances lost to the garbage collector may never be destroyed.
+   * </p>
+   *
+   * @param obj the instance to be destroyed
+   * @throws Exception should be avoided as it may be swallowed by
+   *    the pool implementation.
+   * @see #validateObject
+   * @see ObjectPool#invalidateObject
+   */
+  void destroyObject(T obj) throws Exception;
+
+  /**
+   * Ensures that the instance is safe to be returned by the pool.
+   * Returns <code>false</code> if <code>obj</code> should be destroyed.
+   *
+   * @param obj the instance to be validated
+   * @return <code>false</code> if <code>obj</code> is not valid and should
+   *         be dropped from the pool, <code>true</code> otherwise.
+   */
+  boolean validateObject(T obj);
+
+  /**
+   * Reinitialize an instance to be returned by the pool.
+   *
+   * @param obj the instance to be activated
+   * @throws Exception if there is a problem activating <code>obj</code>,
+   *    this exception may be swallowed by the pool.
+   * @see #destroyObject
+   */
+  void activateObject(T obj) throws Exception;
+
+  /**
+   * Uninitialize an instance to be returned to the idle object pool.
+   *
+   * @param obj the instance to be passivated
+   * @throws Exception if there is a problem passivating <code>obj</code>,
+   *    this exception may be swallowed by the pool.
+   * @see #destroyObject
+   */
+  void passivateObject(T obj) throws Exception;
+}
diff --git a/src/java/org/apache/commons/pool/impl/CursorableLinkedList.java b/src/java/org/apache/commons/pool/impl/CursorableLinkedList.java
new file mode 100644
index 0000000..265fc70
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/CursorableLinkedList.java
@@ -0,0 +1,1516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+/**
+ * <p>
+ * This class has been copied from Commons Collections, version 3.1 in order
+ * to eliminate the dependency of pool on collections.  It has package scope
+ * to prevent its inclusion in the pool public API. The class declaration below
+ * should *not* be changed to public.
+ * </p>
+ *
+ * A doubly-linked list implementation of the {@link List} interface,
+ * supporting a {@link ListIterator} that allows concurrent modifications
+ * to the underlying list.
+ * <p>
+ *
+ * Implements all of the optional {@link List} operations, the
+ * stack/queue/dequeue operations available in {@link java.util.LinkedList}
+ * and supports a {@link ListIterator} that allows concurrent modifications
+ * to the underlying list (see {@link #cursor}).
+ * <p>
+ * <b>Note that this implementation is not synchronized.</b>
+ *
+ * @param <E> the type of elements held in this collection
+ * 
+ * @see java.util.LinkedList
+ *
+ * @version $Revision: 480452 $ $Date: 2006-11-29 00:45:14 -0700 (Wed, 29 Nov 2006) $
+ *
+ * @author Rodney Waldhoff
+ * @author Janek Bogucki
+ * @author Simon Kitching
+ */
+class CursorableLinkedList<E> implements List<E>, Serializable {
+    /** Ensure serialization compatibility */
+    private static final long serialVersionUID = 8836393098519411393L;
+
+    //--- public methods ---------------------------------------------
+    // CHECKSTYLE: stop all checks
+
+    /**
+     * Appends the specified element to the end of this list.
+     *
+     * @param o element to be appended to this list.
+     * @return <tt>true</tt>
+     */
+    public boolean add(E o) {
+        insertListable(_head.prev(),null,o);
+        return true;
+    }
+
+    /**
+     * Inserts the specified element at the specified position in this list.
+     * Shifts the element currently at that position (if any) and any subsequent
+     *  elements to the right (adds one to their indices).
+     *
+     * @param index index at which the specified element is to be inserted.
+     * @param element element to be inserted.
+     *
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this list.
+     * @throws IllegalArgumentException if some aspect of the specified
+     *         element prevents it from being added to this list.
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *         (index < 0 || index > size()).
+     */
+    public void add(int index, E element) {
+        if(index == _size) {
+            add(element);
+        } else {
+            if(index < 0 || index > _size) {
+                throw new IndexOutOfBoundsException(String.valueOf(index) + " < 0 or " + String.valueOf(index) + " > " + _size);
+            }
+            Listable<E> succ = (isEmpty() ? null : getListableAt(index));
+            Listable<E> pred = (null == succ ? null : succ.prev());
+            insertListable(pred,succ,element);
+        }
+    }
+
+    /**
+     * Appends all of the elements in the specified collection to the end of
+     * this list, in the order that they are returned by the specified
+     * {@link Collection}'s {@link Iterator}.  The behavior of this operation is
+     * unspecified if the specified collection is modified while
+     * the operation is in progress.  (Note that this will occur if the
+     * specified collection is this list, and it's nonempty.)
+     *
+     * @param c collection whose elements are to be added to this list.
+     * @return <tt>true</tt> if this list changed as a result of the call.
+     *
+     * @throws ClassCastException if the class of an element in the specified
+     *       collection prevents it from being added to this list.
+     * @throws IllegalArgumentException if some aspect of an element in the
+     *         specified collection prevents it from being added to this
+     *         list.
+     */
+    public boolean addAll(Collection<? extends E> c) {
+        if(c.isEmpty()) {
+            return false;
+        }
+        Iterator<? extends E> it = c.iterator();
+        while(it.hasNext()) {
+            insertListable(_head.prev(),null,it.next());
+        }
+        return true;
+    }
+
+    /**
+     * Inserts all of the elements in the specified collection into this
+     * list at the specified position.  Shifts the element currently at
+     * that position (if any) and any subsequent elements to the right
+     * (increases their indices).  The new elements will appear in this
+     * list in the order that they are returned by the specified
+     * {@link Collection}'s {@link Iterator}.  The behavior of this operation is
+     * unspecified if the specified collection is modified while the
+     * operation is in progress.  (Note that this will occur if the specified
+     * collection is this list, and it's nonempty.)
+     *
+     * @param index index at which to insert first element from the specified
+     *              collection.
+     * @param c elements to be inserted into this list.
+     * @return <tt>true</tt> if this list changed as a result of the call.
+     *
+     * @throws ClassCastException if the class of one of elements of the
+     *         specified collection prevents it from being added to this
+     *         list.
+     * @throws IllegalArgumentException if some aspect of one of elements of
+     *         the specified collection prevents it from being added to
+     *         this list.
+     * @throws IndexOutOfBoundsException if the index is out of range (index
+     *         < 0 || index > size()).
+     */
+    public boolean addAll(int index, Collection<? extends E> c) {
+        if(c.isEmpty()) {
+            return false;
+        } else if(_size == index || _size == 0) {
+            return addAll(c);
+        } else {
+            Listable<E> succ = getListableAt(index);
+            Listable<E> pred = (null == succ) ? null : succ.prev();
+            Iterator<? extends E> it = c.iterator();
+            while(it.hasNext()) {
+                pred = insertListable(pred,succ,it.next());
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Inserts the specified element at the beginning of this list.
+     * (Equivalent to {@link #add(int,java.lang.Object) <tt>add(0,o)</tt>}).
+     *
+     * @param o element to be prepended to this list.
+     * @return <tt>true</tt>
+     */
+    public boolean addFirst(E o) {
+        insertListable(null,_head.next(),o);
+        return true;
+    }
+
+    /**
+     * Inserts the specified element at the end of this list.
+     * (Equivalent to {@link #add(java.lang.Object)}).
+     *
+     * @param o element to be appended to this list.
+     * @return <tt>true</tt>
+     */
+    public boolean addLast(E o) {
+        insertListable(_head.prev(),null,o);
+        return true;
+    }
+
+    /**
+     * Removes all of the elements from this list.  This
+     * list will be empty after this call returns (unless
+     * it throws an exception).
+     */
+    public void clear() {
+        /*
+        // this is the quick way, but would force us
+        // to break all the cursors
+        _modCount++;
+        _head.setNext(null);
+        _head.setPrev(null);
+        _size = 0;
+        */
+        Iterator<E> it = iterator();
+        while(it.hasNext()) {
+            it.next();
+            it.remove();
+        }
+    }
+
+    /**
+     * Returns <tt>true</tt> if this list contains the specified element.
+     * More formally, returns <tt>true</tt> if and only if this list contains
+     * at least one element <tt>e</tt> such that
+     * <tt>(o==null ? e==null : o.equals(e))</tt>.
+     *
+     * @param o element whose presence in this list is to be tested.
+     * @return <tt>true</tt> if this list contains the specified element.
+     */
+    public boolean contains(Object o) {
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            if((null == o && null == elt.value()) ||
+               (o != null && o.equals(elt.value()))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns <tt>true</tt> if this list contains all of the elements of the
+     * specified collection.
+     *
+     * @param c collection to be checked for containment in this list.
+     * @return <tt>true</tt> if this list contains all of the elements of the
+     *         specified collection.
+     */
+    public boolean containsAll(Collection<?> c) {
+        Iterator<?> it = c.iterator();
+        while(it.hasNext()) {
+            if(!this.contains(it.next())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns a {@link ListIterator} for iterating through the
+     * elements of this list. Unlike {@link #iterator}, a cursor
+     * is not bothered by concurrent modifications to the
+     * underlying list.
+     * <p>
+     * Specifically, when elements are added to the list before or
+     * after the cursor, the cursor simply picks them up automatically.
+     * When the "current" (i.e., last returned by {@link ListIterator#next}
+     * or {@link ListIterator#previous}) element of the list is removed,
+     * the cursor automatically adjusts to the change (invalidating the
+     * last returned value--i.e., it cannot be removed).
+     * <p>
+     * Note that the returned {@link ListIterator} does not support the
+     * {@link ListIterator#nextIndex} and {@link ListIterator#previousIndex}
+     * methods (they throw {@link UnsupportedOperationException} when invoked.
+     * <p>
+     * Historical Note: In previous versions of this class, the object
+     * returned from this method was required to be explicitly closed. This
+     * is no longer necessary.
+     *
+     * @see #cursor(int)
+     * @see #listIterator()
+     * @see CursorableLinkedList.Cursor
+     */
+    public CursorableLinkedList<E>.Cursor cursor() {
+        return new Cursor(0);
+    }
+
+    /**
+     * Returns a {@link ListIterator} for iterating through the
+     * elements of this list, initialized such that
+     * {@link ListIterator#next} will return the element at
+     * the specified index (if any) and {@link ListIterator#previous}
+     * will return the element immediately preceding it (if any).
+     * Unlike {@link #iterator}, a cursor
+     * is not bothered by concurrent modifications to the
+     * underlying list.
+     *
+     * @see #cursor()
+     * @see #listIterator(int)
+     * @see CursorableLinkedList.Cursor
+     * @throws IndexOutOfBoundsException if the index is out of range (index
+     *          < 0 || index > size()).
+     */
+    public CursorableLinkedList<E>.Cursor cursor(int i) {
+        return new Cursor(i);
+    }
+
+    /**
+     * Compares the specified object with this list for equality.  Returns
+     * <tt>true</tt> if and only if the specified object is also a list, both
+     * lists have the same size, and all corresponding pairs of elements in
+     * the two lists are <i>equal</i>.  (Two elements <tt>e1</tt> and
+     * <tt>e2</tt> are <i>equal</i> if <tt>(e1==null ? e2==null :
+     * e1.equals(e2))</tt>.)  In other words, two lists are defined to be
+     * equal if they contain the same elements in the same order.  This
+     * definition ensures that the equals method works properly across
+     * different implementations of the <tt>List</tt> interface.
+     *
+     * @param o the object to be compared for equality with this list.
+     * @return <tt>true</tt> if the specified object is equal to this list.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if(o == this) {
+            return true;
+        } else if(!(o instanceof List)) {
+            return false;
+        }
+        Iterator<?> it = ((List<?>)o).listIterator();
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            if(!it.hasNext() || (null == elt.value() ? null != it.next() : !(elt.value().equals(it.next()))) ) {
+                return false;
+            }
+        }
+        return !it.hasNext();
+    }
+
+    /**
+     * Returns the element at the specified position in this list.
+     *
+     * @param index index of element to return.
+     * @return the element at the specified position in this list.
+     *
+     * @throws IndexOutOfBoundsException if the index is out of range (index
+     *         < 0 || index >= size()).
+     */
+    public E get(int index) {
+        return getListableAt(index).value();
+    }
+
+    /**
+     * Returns the element at the beginning of this list.
+     */
+    public E getFirst() {
+        try {
+            return _head.next().value();
+        } catch(NullPointerException e) {
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * Returns the element at the end of this list.
+     */
+    public E getLast() {
+        try {
+            return _head.prev().value();
+        } catch(NullPointerException e) {
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * Returns the hash code value for this list.  The hash code of a list
+     * is defined to be the result of the following calculation:
+     * <pre>
+     *  hashCode = 1;
+     *  Iterator i = list.iterator();
+     *  while (i.hasNext()) {
+     *      Object obj = i.next();
+     *      hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
+     *  }
+     * </pre>
+     * This ensures that <tt>list1.equals(list2)</tt> implies that
+     * <tt>list1.hashCode()==list2.hashCode()</tt> for any two lists,
+     * <tt>list1</tt> and <tt>list2</tt>, as required by the general
+     * contract of <tt>Object.hashCode</tt>.
+     *
+     * @return the hash code value for this list.
+     * @see Object#hashCode()
+     * @see Object#equals(Object)
+     * @see #equals(Object)
+     */
+    @Override
+    public int hashCode() {
+        int hash = 1;
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            hash = 31*hash + (null == elt.value() ? 0 : elt.value().hashCode());
+        }
+        return hash;
+    }
+
+    /**
+     * Returns the index in this list of the first occurrence of the specified
+     * element, or -1 if this list does not contain this element.
+     * More formally, returns the lowest index <tt>i</tt> such that
+     * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
+     * or -1 if there is no such index.
+     *
+     * @param o element to search for.
+     * @return the index in this list of the first occurrence of the specified
+     *         element, or -1 if this list does not contain this element.
+     */
+    public int indexOf(Object o) {
+        int ndx = 0;
+
+        // perform the null check outside of the loop to save checking every
+        // single time through the loop.
+        if (null == o) {
+            for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+                if (null == elt.value()) {
+                    return ndx;
+                }
+                ndx++;
+            }
+        } else {
+
+            for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+                if (o.equals(elt.value())) {
+                    return ndx;
+                }
+                ndx++;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns <tt>true</tt> if this list contains no elements.
+     * @return <tt>true</tt> if this list contains no elements.
+     */
+    public boolean isEmpty() {
+        return(0 == _size);
+    }
+
+    /**
+     * Returns a fail-fast iterator.
+     * @see List#iterator
+     */
+    public Iterator<E> iterator() {
+        return listIterator(0);
+    }
+
+    /**
+     * Returns the index in this list of the last occurrence of the specified
+     * element, or -1 if this list does not contain this element.
+     * More formally, returns the highest index <tt>i</tt> such that
+     * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
+     * or -1 if there is no such index.
+     *
+     * @param o element to search for.
+     * @return the index in this list of the last occurrence of the specified
+     *         element, or -1 if this list does not contain this element.
+     */
+    public int lastIndexOf(Object o) {
+        int ndx = _size-1;
+
+        // perform the null check outside of the loop to save checking every
+        // single time through the loop.
+        if (null == o) {
+            for(Listable<E> elt = _head.prev(), past = null; null != elt && past != _head.next(); elt = (past = elt).prev()) {
+                if (null == elt.value()) {
+                    return ndx;
+                }
+                ndx--;
+            }
+        } else {
+            for(Listable<E> elt = _head.prev(), past = null; null != elt && past != _head.next(); elt = (past = elt).prev()) {
+                if (o.equals(elt.value())) {
+                    return ndx;
+                }
+                ndx--;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns a fail-fast ListIterator.
+     * @see List#listIterator()
+     */
+    public ListIterator<E> listIterator() {
+        return listIterator(0);
+    }
+
+    /**
+     * Returns a fail-fast ListIterator.
+     * @see List#listIterator(int)
+     */
+    public ListIterator<E> listIterator(int index) {
+        if(index<0 || index > _size) {
+            throw new IndexOutOfBoundsException(index + " < 0 or > " + _size);
+        }
+        return new ListIter(index);
+    }
+
+    /**
+     * Removes the first occurrence in this list of the specified element.
+     * If this list does not contain the element, it is
+     * unchanged.  More formally, removes the element with the lowest index i
+     * such that <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> (if
+     * such an element exists).
+     *
+     * @param o element to be removed from this list, if present.
+     * @return <tt>true</tt> if this list contained the specified element.
+     */
+    public boolean remove(Object o) {
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            if(null == o && null == elt.value()) {
+                removeListable(elt);
+                return true;
+            } else if(o != null && o.equals(elt.value())) {
+                removeListable(elt);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Removes the element at the specified position in this list (optional
+     * operation).  Shifts any subsequent elements to the left (subtracts one
+     * from their indices).  Returns the element that was removed from the
+     * list.
+     *
+     * @param index the index of the element to removed.
+     * @return the element previously at the specified position.
+     *
+     * @throws IndexOutOfBoundsException if the index is out of range (index
+     *            < 0 || index >= size()).
+     */
+    public E remove(int index) {
+        Listable<E> elt = getListableAt(index);
+        E ret = elt.value();
+        removeListable(elt);
+        return ret;
+    }
+
+    /**
+     * Removes from this list all the elements that are contained in the
+     * specified collection.
+     *
+     * @param c collection that defines which elements will be removed from
+     *          this list.
+     * @return <tt>true</tt> if this list changed as a result of the call.
+     */
+    public boolean removeAll(Collection<?> c) {
+        if(0 == c.size() || 0 == _size) {
+            return false;
+        } else {
+            boolean changed = false;
+            Iterator<?> it = iterator();
+            while(it.hasNext()) {
+                if(c.contains(it.next())) {
+                    it.remove();
+                    changed = true;
+                }
+            }
+            return changed;
+        }
+    }
+
+    /**
+     * Removes the first element of this list, if any.
+     */
+    public E removeFirst() {
+        if(_head.next() != null) {
+            E val = _head.next().value();
+            removeListable(_head.next());
+            return val;
+        } else {
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * Removes the last element of this list, if any.
+     */
+    public E removeLast() {
+        if(_head.prev() != null) {
+            E val = _head.prev().value();
+            removeListable(_head.prev());
+            return val;
+        } else {
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * Retains only the elements in this list that are contained in the
+     * specified collection.  In other words, removes
+     * from this list all the elements that are not contained in the specified
+     * collection.
+     *
+     * @param c collection that defines which elements this set will retain.
+     *
+     * @return <tt>true</tt> if this list changed as a result of the call.
+     */
+    public boolean retainAll(Collection<?> c) {
+        boolean changed = false;
+        Iterator<?> it = iterator();
+        while(it.hasNext()) {
+            if(!c.contains(it.next())) {
+                it.remove();
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    /**
+     * Replaces the element at the specified position in this list with the
+     * specified element.
+     *
+     * @param index index of element to replace.
+     * @param element element to be stored at the specified position.
+     * @return the element previously at the specified position.
+     *
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this list.
+     * @throws IllegalArgumentException if some aspect of the specified
+     *         element prevents it from being added to this list.
+     * @throws IndexOutOfBoundsException if the index is out of range
+     *         (index < 0 || index >= size()).
+     */
+    public E set(int index, E element) {
+        Listable<E> elt = getListableAt(index);
+        E val = elt.setValue(element);
+        broadcastListableChanged(elt);
+        return val;
+    }
+
+    /**
+     * Returns the number of elements in this list.
+     * @return the number of elements in this list.
+     */
+    public int size() {
+        return _size;
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list in proper
+     * sequence.  Obeys the general contract of the {@link Collection#toArray()} method.
+     *
+     * @return an array containing all of the elements in this list in proper
+     *         sequence.
+     */
+    public Object[] toArray() {
+        Object[] array = new Object[_size];
+        int i = 0;
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            array[i++] = elt.value();
+        }
+        return array;
+    }
+
+    /**
+     * Returns an array containing all of the elements in this list in proper
+     * sequence; the runtime type of the returned array is that of the
+     * specified array. Obeys the general contract of the
+     * {@link Collection#toArray()} method.
+     *
+     * @param a      the array into which the elements of this list are to
+     *               be stored, if it is big enough; otherwise, a new array of the
+     *               same runtime type is allocated for this purpose.
+     * @return an array containing the elements of this list.
+     * @exception ArrayStoreException
+     *                   if the runtime type of the specified array
+     *                   is not a supertype of the runtime type of every element in
+     *                   this list.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T[] toArray(T a[]) {
+        if(a.length < _size) {
+            a = (T[])Array.newInstance(a.getClass().getComponentType(), _size);
+        }
+        int i = 0;
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            a[i++] = (T) elt.value();
+        }
+        if(a.length > _size) {
+            a[_size] = null; // should we null out the rest of the array also? java.util.LinkedList doesn't
+        }
+        return a;
+    }
+
+    /**
+     * Returns a {@link String} representation of this list, suitable for debugging.
+     * @return a {@link String} representation of this list, suitable for debugging.
+     */
+    @Override
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("[");
+        for(Listable<E> elt = _head.next(), past = null; null != elt && past != _head.prev(); elt = (past = elt).next()) {
+            if(_head.next() != elt) {
+                buf.append(", ");
+            }
+            buf.append(elt.value());
+        }
+        buf.append("]");
+        return buf.toString();
+    }
+
+    /**
+     * Returns a fail-fast sublist.
+     * @see List#subList(int,int)
+     */
+    public List<E> subList(int i, int j) {
+        if(i < 0 || j > _size || i > j) {
+            throw new IndexOutOfBoundsException();
+        } else if(i == 0 && j == _size) {
+            return this;
+        } else {
+            return new CursorableSubList<E>(this,i,j);
+        }
+    }
+
+    //--- protected methods ------------------------------------------
+
+    /**
+     * Inserts a new <i>value</i> into my
+     * list, after the specified <i>before</i> element, and before the
+     * specified <i>after</i> element
+     *
+     * @return the newly created
+     * {@link org.apache.commons.collections.CursorableLinkedList.Listable}
+     */
+    protected Listable<E> insertListable(Listable<E> before, Listable<E> after, E value) {
+        _modCount++;
+        _size++;
+        Listable<E> elt = new Listable<E>(before,after,value);
+        if(null != before) {
+            before.setNext(elt);
+        } else {
+            _head.setNext(elt);
+        }
+
+        if(null != after) {
+            after.setPrev(elt);
+        } else {
+            _head.setPrev(elt);
+        }
+        broadcastListableInserted(elt);
+        return elt;
+    }
+
+    /**
+     * Removes the given
+     * {@link org.apache.commons.collections.CursorableLinkedList.Listable}
+     * from my list.
+     */
+    protected void removeListable(Listable<E> elt) {
+        _modCount++;
+        _size--;
+        if(_head.next() == elt) {
+            _head.setNext(elt.next());
+        }
+        if(null != elt.next()) {
+            elt.next().setPrev(elt.prev());
+        }
+        if(_head.prev() == elt) {
+            _head.setPrev(elt.prev());
+        }
+        if(null != elt.prev()) {
+            elt.prev().setNext(elt.next());
+        }
+        broadcastListableRemoved(elt);
+    }
+
+    /**
+     * Returns the
+     * {@link org.apache.commons.collections.CursorableLinkedList.Listable}
+     * at the specified index.
+     *
+     * @throws IndexOutOfBoundsException if index is less than zero or
+     *         greater than or equal to the size of this list.
+     */
+    protected Listable<E> getListableAt(int index) {
+        if(index < 0 || index >= _size) {
+            throw new IndexOutOfBoundsException(String.valueOf(index) + " < 0 or " + String.valueOf(index) + " >= " + _size);
+        }
+        if(index <=_size/2) {
+            Listable<E> elt = _head.next();
+            for(int i = 0; i < index; i++) {
+                elt = elt.next();
+            }
+            return elt;
+        } else {
+            Listable<E> elt = _head.prev();
+            for(int i = (_size-1); i > index; i--) {
+                elt = elt.prev();
+            }
+            return elt;
+        }
+    }
+
+    /**
+     * Registers a {@link CursorableLinkedList.Cursor} to be notified
+     * of changes to this list.
+     */
+    protected void registerCursor(Cursor cur) {
+        // We take this opportunity to clean the _cursors list
+        // of WeakReference objects to garbage-collected cursors.
+        for (Iterator<WeakReference<Cursor>> it = _cursors.iterator(); it.hasNext(); ) {
+            WeakReference<Cursor> ref = it.next();
+            if (ref.get() == null) {
+                it.remove();
+            }
+        }
+
+        _cursors.add( new WeakReference<Cursor>(cur) );
+    }
+
+    /**
+     * Removes a {@link CursorableLinkedList.Cursor} from
+     * the set of cursors to be notified of changes to this list.
+     */
+    protected void unregisterCursor(Cursor cur) {
+        for (Iterator<WeakReference<Cursor>> it = _cursors.iterator(); it.hasNext(); ) {
+            WeakReference<Cursor> ref = it.next();
+            Cursor cursor = ref.get();
+            if (cursor == null) {
+                // some other unrelated cursor object has been
+                // garbage-collected; let's take the opportunity to
+                // clean up the cursors list anyway..
+                it.remove();
+
+            } else if (cursor == cur) {
+                ref.clear();
+                it.remove();
+                break;
+            }
+        }
+    }
+
+    /**
+     * Informs all of my registered cursors that they are now
+     * invalid.
+     */
+    protected void invalidateCursors() {
+        Iterator<WeakReference<Cursor>> it = _cursors.iterator();
+        while (it.hasNext()) {
+            WeakReference<Cursor> ref = it.next();
+            Cursor cursor = ref.get();
+            if (cursor != null) {
+                // cursor is null if object has been garbage-collected
+                cursor.invalidate();
+                ref.clear();
+            }
+            it.remove();
+        }
+    }
+
+    /**
+     * Informs all of my registered cursors that the specified
+     * element was changed.
+     * @see #set(int,java.lang.Object)
+     */
+    protected void broadcastListableChanged(Listable<E> elt) {
+        Iterator<WeakReference<Cursor>> it = _cursors.iterator();
+        while (it.hasNext()) {
+            WeakReference<Cursor> ref = it.next();
+            Cursor cursor = ref.get();
+            if (cursor == null) {
+                it.remove(); // clean up list
+            } else {
+                cursor.listableChanged(elt);
+            }
+        }
+    }
+
+    /**
+     * Informs all of my registered cursors that the specified
+     * element was just removed from my list.
+     */
+    protected void broadcastListableRemoved(Listable<E> elt) {
+        Iterator<WeakReference<Cursor>> it = _cursors.iterator();
+        while (it.hasNext()) {
+            WeakReference<Cursor> ref = it.next();
+            Cursor cursor = ref.get();
+            if (cursor == null) {
+                it.remove(); // clean up list
+            } else {
+                cursor.listableRemoved(elt);
+            }
+        }
+    }
+
+    /**
+     * Informs all of my registered cursors that the specified
+     * element was just added to my list.
+     */
+    protected void broadcastListableInserted(Listable<E> elt) {
+        Iterator<WeakReference<Cursor>> it = _cursors.iterator();
+        while (it.hasNext()) {
+            WeakReference<Cursor> ref = it.next();
+            Cursor cursor = ref.get();
+            if (cursor == null) {
+                it.remove();  // clean up list
+            } else {
+                cursor.listableInserted(elt);
+            }
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        out.writeInt(_size);
+        Listable<E> cur = _head.next();
+        while (cur != null) {
+            out.writeObject(cur.value());
+            cur = cur.next();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        _size = 0;
+        _modCount = 0;
+        _cursors = new ArrayList<WeakReference<Cursor>>();
+        _head = new Listable<E>(null,null,null);
+        int size = in.readInt();
+        for (int i=0;i<size;i++) {
+            this.add((E)in.readObject());
+        }
+    }
+
+    //--- protected attributes ---------------------------------------
+
+    /** The number of elements in me. */
+    protected transient int _size = 0;
+
+    /**
+     * A sentry node.
+     * <p>
+     * <tt>_head.next()</tt> points to the first element in the list,
+     * <tt>_head.prev()</tt> to the last. Note that it is possible for
+     * <tt>_head.next().prev()</tt> and <tt>_head.prev().next()</tt> to be
+     * non-null, as when I am a sublist for some larger list.
+     * Use <tt>== _head.next()</tt> and <tt>== _head.prev()</tt> to determine
+     * if a given
+     * {@link org.apache.commons.collections.CursorableLinkedList.Listable}
+     * is the first or last element in the list.
+     */
+    protected transient Listable<E> _head = new Listable<E>(null,null,null);
+
+    /** Tracks the number of structural modifications to me. */
+    protected transient int _modCount = 0;
+
+    /**
+     * A list of the currently {@link CursorableLinkedList.Cursor}s currently
+     * open in this list.
+     */
+    protected transient List<WeakReference<Cursor>> _cursors = new ArrayList<WeakReference<Cursor>>();
+
+    //--- inner classes ----------------------------------------------
+
+    static class Listable<E> implements Serializable {
+        private Listable<E> _prev = null;
+        private Listable<E> _next = null;
+        private E _val = null;
+
+        Listable(Listable<E> prev, Listable<E> next, E val) {
+            _prev = prev;
+            _next = next;
+            _val = val;
+        }
+
+        Listable<E> next() {
+            return _next;
+        }
+
+        Listable<E> prev() {
+            return _prev;
+        }
+
+        E value() {
+            return _val;
+        }
+
+        void setNext(Listable<E> next) {
+            _next = next;
+        }
+
+        void setPrev(Listable<E> prev) {
+            _prev = prev;
+        }
+
+        E setValue(E val) {
+            E temp = _val;
+            _val = val;
+            return temp;
+        }
+    }
+
+    class ListIter implements ListIterator<E> {
+        Listable<E> _cur = null;
+        Listable<E> _lastReturned = null;
+        int _expectedModCount = _modCount;
+        int _nextIndex = 0;
+
+        ListIter(int index) {
+            if(index == 0) {
+                _cur = new Listable<E>(null,_head.next(),null);
+                _nextIndex = 0;
+            } else if(index == _size) {
+                _cur = new Listable<E>(_head.prev(),null,null);
+                _nextIndex = _size;
+            } else {
+                Listable<E> temp = getListableAt(index);
+                _cur = new Listable<E>(temp.prev(),temp,null);
+                _nextIndex = index;
+            }
+        }
+
+        public E previous() {
+            checkForComod();
+            if(!hasPrevious()) {
+                throw new NoSuchElementException();
+            } else {
+                E ret = _cur.prev().value();
+                _lastReturned = _cur.prev();
+                _cur.setNext(_cur.prev());
+                _cur.setPrev(_cur.prev().prev());
+                _nextIndex--;
+                return ret;
+            }
+        }
+
+        public boolean hasNext() {
+            checkForComod();
+            return(null != _cur.next() && _cur.prev() != _head.prev());
+        }
+
+        public E next() {
+            checkForComod();
+            if(!hasNext()) {
+                throw new NoSuchElementException();
+            } else {
+                E ret = _cur.next().value();
+                _lastReturned = _cur.next();
+                _cur.setPrev(_cur.next());
+                _cur.setNext(_cur.next().next());
+                _nextIndex++;
+                return ret;
+            }
+        }
+
+        public int previousIndex() {
+            checkForComod();
+            if(!hasPrevious()) {
+                return -1;
+            }
+            return _nextIndex-1;
+        }
+
+        public boolean hasPrevious() {
+            checkForComod();
+            return(null != _cur.prev() && _cur.next() != _head.next());
+        }
+
+        public void set(E o) {
+            checkForComod();
+            try {
+                _lastReturned.setValue(o);
+            } catch(NullPointerException e) {
+                throw new IllegalStateException();
+            }
+        }
+
+        public int nextIndex() {
+            checkForComod();
+            if(!hasNext()) {
+                return size();
+            }
+            return _nextIndex;
+        }
+
+        public void remove() {
+            checkForComod();
+            if(null == _lastReturned) {
+                throw new IllegalStateException();
+            } else {
+                _cur.setNext(_lastReturned == _head.prev() ? null : _lastReturned.next());
+                _cur.setPrev(_lastReturned == _head.next() ? null : _lastReturned.prev());
+                removeListable(_lastReturned);
+                _lastReturned = null;
+                _nextIndex--;
+                _expectedModCount++;
+            }
+        }
+
+        public void add(E o) {
+            checkForComod();
+            _cur.setPrev(insertListable(_cur.prev(),_cur.next(),o));
+            _lastReturned = null;
+            _nextIndex++;
+            _expectedModCount++;
+        }
+
+        protected void checkForComod() {
+            if(_expectedModCount != _modCount) {
+                throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    public class Cursor extends ListIter implements ListIterator<E> {
+        boolean _valid = false;
+
+        Cursor(int index) {
+            super(index);
+            _valid = true;
+            registerCursor(this);
+        }
+
+        @Override
+        public int previousIndex() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int nextIndex() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void add(E o) {
+            checkForComod();
+            Listable<E> elt = insertListable(_cur.prev(),_cur.next(),o);
+            _cur.setPrev(elt);
+            _cur.setNext(elt.next());
+            _lastReturned = null;
+            _nextIndex++;
+            _expectedModCount++;
+        }
+
+        protected void listableRemoved(Listable<E> elt) {
+            if(null == _head.prev()) {
+                _cur.setNext(null);
+            } else if(_cur.next() == elt) {
+                _cur.setNext(elt.next());
+            }
+            if(null == _head.next()) {
+                _cur.setPrev(null);
+            } else if(_cur.prev() == elt) {
+                _cur.setPrev(elt.prev());
+            }
+            if(_lastReturned == elt) {
+                _lastReturned = null;
+            }
+        }
+
+        protected void listableInserted(Listable<E> elt) {
+            if(null == _cur.next() && null == _cur.prev()) {
+                _cur.setNext(elt);
+            } else if(_cur.prev() == elt.prev()) {
+                _cur.setNext(elt);
+            }
+            if(_cur.next() == elt.next()) {
+                _cur.setPrev(elt);
+            }
+            if(_lastReturned == elt) {
+                _lastReturned = null;
+            }
+        }
+
+        protected void listableChanged(Listable<E> elt) {
+            if(_lastReturned == elt) {
+                _lastReturned = null;
+            }
+        }
+
+        @Override
+        protected void checkForComod() {
+            if(!_valid) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        protected void invalidate() {
+            _valid = false;
+        }
+
+        /**
+         * Mark this cursor as no longer being needed. Any resources
+         * associated with this cursor are immediately released.
+         * In previous versions of this class, it was mandatory to close
+         * all cursor objects to avoid memory leaks. It is <i>no longer</i>
+         * necessary to call this close method; an instance of this class
+         * can now be treated exactly like a normal iterator.
+         */
+        public void close() {
+            if(_valid) {
+                _valid = false;
+                unregisterCursor(this);
+            }
+        }
+    }
+
+}
+
+class CursorableSubList<E> extends CursorableLinkedList<E> implements List<E> {
+
+    //--- constructors -----------------------------------------------
+
+    CursorableSubList(CursorableLinkedList<E> list, int from, int to) {
+        if(0 > from || list.size() < to) {
+            throw new IndexOutOfBoundsException();
+        } else if(from > to) {
+            throw new IllegalArgumentException();
+        }
+        _list = list;
+        if(from < list.size()) {
+            _head.setNext(_list.getListableAt(from));
+            _pre = (null == _head.next()) ? null : _head.next().prev();
+        } else {
+            _pre = _list.getListableAt(from-1);
+        }
+        if(from == to) {
+            _head.setNext(null);
+            _head.setPrev(null);
+            if(to < list.size()) {
+                _post = _list.getListableAt(to);
+            } else {
+                _post = null;
+            }
+        } else {
+            _head.setPrev(_list.getListableAt(to-1));
+            _post = _head.prev().next();
+        }
+        _size = to - from;
+        _modCount = _list._modCount;
+    }
+
+    //--- public methods ------------------------------------------
+
+    @Override
+    public void clear() {
+        checkForComod();
+        Iterator<E> it = iterator();
+        while(it.hasNext()) {
+            it.next();
+            it.remove();
+        }
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        checkForComod();
+        return super.iterator();
+    }
+
+    @Override
+    public int size() {
+        checkForComod();
+        return super.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        checkForComod();
+        return super.isEmpty();
+    }
+
+    @Override
+    public Object[] toArray() {
+        checkForComod();
+        return super.toArray();
+    }
+
+    @Override
+    public <T> T[] toArray(T a[]) {
+        checkForComod();
+        return super.toArray(a);
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        checkForComod();
+        return super.contains(o);
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        checkForComod();
+        return super.remove(o);
+    }
+
+    @Override
+    public E removeFirst() {
+        checkForComod();
+        return super.removeFirst();
+    }
+
+    @Override
+    public E removeLast() {
+        checkForComod();
+        return super.removeLast();
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends E> c) {
+        checkForComod();
+        return super.addAll(c);
+    }
+
+    @Override
+    public boolean add(E o) {
+        checkForComod();
+        return super.add(o);
+    }
+
+    @Override
+    public boolean addFirst(E o) {
+        checkForComod();
+        return super.addFirst(o);
+    }
+
+    @Override
+    public boolean addLast(E o) {
+        checkForComod();
+        return super.addLast(o);
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        checkForComod();
+        return super.removeAll(c);
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        checkForComod();
+        return super.containsAll(c);
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends E> c) {
+        checkForComod();
+        return super.addAll(index,c);
+    }
+
+    @Override
+    public int hashCode() {
+        checkForComod();
+        return super.hashCode();
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        checkForComod();
+        return super.retainAll(c);
+    }
+
+    @Override
+    public E set(int index, E element) {
+        checkForComod();
+        return super.set(index,element);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        checkForComod();
+        return super.equals(o);
+    }
+
+    @Override
+    public E get(int index) {
+        checkForComod();
+        return super.get(index);
+    }
+
+    @Override
+    public E getFirst() {
+        checkForComod();
+        return super.getFirst();
+    }
+
+    @Override
+    public E getLast() {
+        checkForComod();
+        return super.getLast();
+    }
+
+    @Override
+    public void add(int index, E element) {
+        checkForComod();
+        super.add(index,element);
+    }
+
+    @Override
+    public ListIterator<E> listIterator(int index) {
+        checkForComod();
+        return super.listIterator(index);
+    }
+
+    @Override
+    public E remove(int index) {
+        checkForComod();
+        return super.remove(index);
+    }
+
+    @Override
+    public int indexOf(Object o) {
+        checkForComod();
+        return super.indexOf(o);
+    }
+
+    @Override
+    public int lastIndexOf(Object o) {
+        checkForComod();
+        return super.lastIndexOf(o);
+    }
+
+    @Override
+    public ListIterator<E> listIterator() {
+        checkForComod();
+        return super.listIterator();
+    }
+
+    @Override
+    public List<E> subList(int fromIndex, int toIndex) {
+        checkForComod();
+        return super.subList(fromIndex,toIndex);
+    }
+
+    //--- protected methods ------------------------------------------
+
+    /**
+     * Inserts a new <i>value</i> into my
+     * list, after the specified <i>before</i> element, and before the
+     * specified <i>after</i> element
+     *
+     * @return the newly created {@link CursorableLinkedList.Listable}
+     */
+    @Override
+    protected Listable<E> insertListable(Listable<E> before, Listable<E> after, E value) {
+        _modCount++;
+        _size++;
+        Listable<E> elt = _list.insertListable((null == before ? _pre : before), (null == after ? _post : after),value);
+        if(null == _head.next()) {
+            _head.setNext(elt);
+            _head.setPrev(elt);
+        }
+        if(before == _head.prev()) {
+            _head.setPrev(elt);
+        }
+        if(after == _head.next()) {
+            _head.setNext(elt);
+        }
+        broadcastListableInserted(elt);
+        return elt;
+    }
+
+    /**
+     * Removes the given {@link CursorableLinkedList.Listable} from my list.
+     */
+    @Override
+    protected void removeListable(Listable<E> elt) {
+        _modCount++;
+        _size--;
+        if(_head.next() == elt && _head.prev() == elt) {
+            _head.setNext(null);
+            _head.setPrev(null);
+        }
+        if(_head.next() == elt) {
+            _head.setNext(elt.next());
+        }
+        if(_head.prev() == elt) {
+            _head.setPrev(elt.prev());
+        }
+        _list.removeListable(elt);
+        broadcastListableRemoved(elt);
+    }
+
+    /**
+     * Test to see if my underlying list has been modified
+     * by some other process.  If it has, throws a
+     * {@link ConcurrentModificationException}, otherwise
+     * quietly returns.
+     *
+     * @throws ConcurrentModificationException
+     */
+    protected void checkForComod() throws ConcurrentModificationException {
+        if(_modCount != _list._modCount) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
+    //--- protected attributes ---------------------------------------
+
+    /** My underlying list */
+    protected CursorableLinkedList<E> _list = null;
+
+    /** The element in my underlying list preceding the first element in my list. */
+    protected Listable<E> _pre = null;
+
+    /** The element in my underlying list following the last element in my list. */
+    protected Listable<E> _post = null;
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/EvictionTimer.java b/src/java/org/apache/commons/pool/impl/EvictionTimer.java
new file mode 100644
index 0000000..e7a42f5
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/EvictionTimer.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * <p>
+ * Provides a shared idle object eviction timer for all pools. This class wraps
+ * the standard {@link Timer} and keeps track of how many pools are using it.
+ * If no pools are using the timer, it is canceled. This prevents a thread
+ * being left running which, in application server environments, can lead to
+ * memory leads and/or prevent applications from shutting down or reloading
+ * cleanly.
+ * </p>
+ * <p>
+ * This class has package scope to prevent its inclusion in the pool public API.
+ * The class declaration below should *not* be changed to public.
+ * </p>
+ */
+class EvictionTimer {
+    
+    /** Timer instance */
+    private static Timer _timer; //@GuardedBy("this")
+    
+    /** Static usage count tracker */
+    private static int _usageCount; //@GuardedBy("this")
+
+    /** Prevent instantiation */
+    private EvictionTimer() {
+        // Hide the default constuctor
+    }
+
+    /**
+     * Add the specified eviction task to the timer. Tasks that are added with a
+     * call to this method *must* call {@link #cancel(TimerTask)} to cancel the
+     * task to prevent memory and/or thread leaks in application server
+     * environments.
+     * @param task      Task to be scheduled
+     * @param delay     Delay in milliseconds before task is executed
+     * @param period    Time in milliseconds between executions
+     */
+    static synchronized void schedule(TimerTask task, long delay, long period) {
+        if (null == _timer) {
+            // Force the new Timer thread to be created with a context class
+            // loader set to the class loader that loaded this library
+            ClassLoader ccl = AccessController.doPrivileged(
+                    new PrivilegedGetTccl());
+            try {
+                AccessController.doPrivileged(new PrivilegedSetTccl(
+                        EvictionTimer.class.getClassLoader()));
+                _timer = new Timer(true);
+            } finally {
+                AccessController.doPrivileged(new PrivilegedSetTccl(ccl));
+            }
+        }
+        _usageCount++;
+        _timer.schedule(task, delay, period);
+    }
+
+    /**
+     * Remove the specified eviction task from the timer.
+     * @param task      Task to be scheduled
+     */
+    static synchronized void cancel(TimerTask task) {
+        task.cancel();
+        _usageCount--;
+        if (_usageCount == 0) {
+            _timer.cancel();
+            _timer = null;
+        }
+    }
+    
+    /** 
+     * {@link PrivilegedAction} used to get the ContextClassLoader
+     */
+    private static class PrivilegedGetTccl implements PrivilegedAction<ClassLoader> {
+
+        /** 
+         * {@inheritDoc}
+         */
+        public ClassLoader run() {
+            return Thread.currentThread().getContextClassLoader();
+        }
+    }
+
+    /** 
+     * {@link PrivilegedAction} used to set the ContextClassLoader
+     */
+    private static class PrivilegedSetTccl implements PrivilegedAction<ClassLoader> {
+
+        /** ClassLoader */
+        private final ClassLoader cl;
+
+        /**
+         * Create a new PrivilegedSetTccl using the given classloader
+         * @param cl ClassLoader to use
+         */
+        PrivilegedSetTccl(ClassLoader cl) {
+            this.cl = cl;
+        }
+
+        /** 
+         * {@inheritDoc}
+         */
+        public ClassLoader run() {
+            Thread.currentThread().setContextClassLoader(cl);
+            return null;
+        }
+    }
+
+}
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..830a30b
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
@@ -0,0 +1,2749 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TimerTask;
+import java.util.TreeMap;
+
+import org.apache.commons.pool.BaseKeyedObjectPool;
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+import org.apache.commons.pool.PoolUtils;
+
+/**
+ * A configurable <code>KeyedObjectPool</code> implementation.
+ * <p>
+ * When coupled with the appropriate {@link KeyedPoolableObjectFactory},
+ * <code>GenericKeyedObjectPool</code> provides robust pooling functionality for
+ * keyed objects. A <code>GenericKeyedObjectPool</code> can be viewed as a map
+ * of pools, keyed on the (unique) key values provided to the
+ * {@link #preparePool preparePool}, {@link #addObject addObject} or
+ * {@link #borrowObject borrowObject} methods. Each time a new key value is
+ * provided to one of these methods, a new pool is created under the given key
+ * to be managed by the containing <code>GenericKeyedObjectPool.</code>
+ * </p>
+ * <p>A <code>GenericKeyedObjectPool</code> provides a number of configurable
+ * parameters:</p>
+ * <ul>
+ *  <li>
+ *    {@link #setMaxActive maxActive} controls the maximum number of objects
+ *    (per key) that can allocated by the pool (checked out to client threads,
+ *    or idle in the pool) at one time.  When non-positive, there is no limit
+ *    to the number of objects per key. When {@link #setMaxActive maxActive} is
+ *    reached, the keyed pool is said to be exhausted.  The default setting for
+ *    this parameter is 8.
+ *  </li>
+ *  <li>
+ *    {@link #setMaxTotal maxTotal} sets a global limit on the number of objects
+ *    that can be in circulation (active or idle) within the combined set of
+ *    pools.  When non-positive, there is no limit to the total number of
+ *    objects in circulation. When {@link #setMaxTotal maxTotal} is exceeded,
+ *    all keyed pools are exhausted. When <code>maxTotal</code> is set to a
+ *    positive value and {@link #borrowObject borrowObject} is invoked
+ *    when at the limit with no idle instances available, an attempt is made to
+ *    create room by clearing the oldest 15% of the elements from the keyed
+ *    pools. The default setting for this parameter is -1 (no limit).
+ *  </li>
+ *  <li>
+ *    {@link #setMaxIdle maxIdle} controls the maximum number of objects that can
+ *    sit idle in the pool (per key) at any time.  When negative, there
+ *    is no limit to the number of objects that may be idle per key. The
+ *    default setting for this parameter is 8.
+ *  </li>
+ *  <li>
+ *    {@link #setWhenExhaustedAction whenExhaustedAction} specifies the
+ *    behavior of the {@link #borrowObject borrowObject} method when a keyed
+ *    pool is exhausted:
+ *    <ul>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction whenExhaustedAction} is
+ *      {@link #WHEN_EXHAUSTED_FAIL}, {@link #borrowObject borrowObject} will throw
+ *      a {@link NoSuchElementException}
+ *    </li>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction whenExhaustedAction} is
+ *      {@link #WHEN_EXHAUSTED_GROW}, {@link #borrowObject borrowObject} will create a new
+ *      object and return it (essentially making {@link #setMaxActive maxActive}
+ *      meaningless.)
+ *    </li>
+ *    <li>
+ *      When {@link #setWhenExhaustedAction whenExhaustedAction}
+ *      is {@link #WHEN_EXHAUSTED_BLOCK}, {@link #borrowObject borrowObject} will block
+ *      (invoke {@link Object#wait() wait} until a new or idle object is available.
+ *      If a positive {@link #setMaxWait maxWait}
+ *      value is supplied, the {@link #borrowObject borrowObject} will block for at
+ *      most that many milliseconds, after which a {@link NoSuchElementException}
+ *      will be thrown.  If {@link #setMaxWait maxWait} is non-positive,
+ *      the {@link #borrowObject borrowObject} method will block indefinitely.
+ *    </li>
+ *    </ul>
+ *    The default <code>whenExhaustedAction</code> setting is
+ *    {@link #WHEN_EXHAUSTED_BLOCK}.
+ *  </li>
+ *  <li>
+ *    When {@link #setTestOnBorrow testOnBorrow} is set, the pool will
+ *    attempt to validate each object before it is returned from the
+ *    {@link #borrowObject borrowObject} method. (Using the provided factory's
+ *    {@link KeyedPoolableObjectFactory#validateObject validateObject} method.)
+ *    Objects that fail to validate will be dropped from the pool, and a
+ *    different object will be borrowed. The default setting for this parameter
+ *    is <code>false.</code>
+ *  </li>
+ *  <li>
+ *    When {@link #setTestOnReturn testOnReturn} is set, the pool will
+ *    attempt to validate each object before it is returned to the pool in the
+ *    {@link #returnObject returnObject} method. (Using the provided factory's
+ *    {@link KeyedPoolableObjectFactory#validateObject validateObject}
+ *    method.)  Objects that fail to validate will be dropped from the pool.
+ *    The default setting for this parameter is <code>false.</code>
+ *  </li>
+ * </ul>
+ * <p>
+ * Optionally, one may configure the pool to examine and possibly evict objects
+ * as they sit idle in the pool and to ensure that a minimum number of idle
+ * objects is maintained for each key. This is performed by an
+ * "idle object eviction" thread, which runs asynchronously. Caution should be
+ * used when configuring this optional feature. Eviction runs contend with client
+ * threads for access to objects in the pool, so if they run too frequently
+ * performance issues may result.  The idle object eviction thread may be
+ * configured using the following attributes:
+ * <ul>
+ *  <li>
+ *   {@link #setTimeBetweenEvictionRunsMillis timeBetweenEvictionRunsMillis}
+ *   indicates how long the eviction thread should sleep before "runs" of examining
+ *   idle objects.  When non-positive, no eviction thread will be launched. The
+ *   default setting for this parameter is -1 (i.e., by default, idle object
+ *   eviction is disabled).
+ *  </li>
+ *  <li>
+ *   {@link #setMinEvictableIdleTimeMillis minEvictableIdleTimeMillis}
+ *   specifies the minimum amount of time that an object may sit idle in the
+ *   pool before it is eligible for eviction due to idle time.  When
+ *   non-positive, no object will be dropped from the pool due to idle time
+ *   alone.  This setting has no effect unless
+ *   <code>timeBetweenEvictionRunsMillis > 0.</code>  The default setting
+ *   for this parameter is 30 minutes.
+ *  </li>
+ *  <li>
+ *   {@link #setTestWhileIdle testWhileIdle} indicates whether or not idle
+ *   objects should be validated using the factory's
+ *   {@link KeyedPoolableObjectFactory#validateObject validateObject} method
+ *   during idle object eviction runs.  Objects that fail to validate will be
+ *   dropped from the pool. This setting has no effect unless
+ *   <code>timeBetweenEvictionRunsMillis > 0.</code>  The default setting
+ *   for this parameter is <code>false.</code>
+ *  </li>
+ *  <li>
+ *    {@link #setMinIdle minIdle} sets a target value for the minimum number of
+ *    idle objects (per key) that should always be available. If this parameter
+ *    is set to a positive number and
+ *    <code>timeBetweenEvictionRunsMillis > 0,</code> each time the idle object
+ *    eviction thread runs, it will try to create enough idle instances so that
+ *    there will be <code>minIdle</code> idle instances available under each
+ *    key. This parameter is also used by {@link #preparePool preparePool}
+ *    if <code>true</code> is provided as that method's
+ *    <code>populateImmediately</code> parameter. The default setting for this
+ *    parameter is 0.
+ *  </li>
+ * </ul>
+ * <p>
+ * The pools can be configured to behave as LIFO queues with respect to idle
+ * objects - always returning the most recently used object from the pool,
+ * or as FIFO queues, where borrowObject always returns the oldest object
+ * in the idle object pool.
+ * <ul>
+ *  <li>
+ *   {@link #setLifo <i>Lifo</i>}
+ *   determines whether or not the pools return idle objects in
+ *   last-in-first-out order. The default setting for this parameter is
+ *   <code>true.</code>
+ *  </li>
+ * </ul>
+ * <p>
+ * GenericKeyedObjectPool is not usable without a {@link KeyedPoolableObjectFactory}.  A
+ * non-<code>null</code> factory must be provided either as a constructor argument
+ * or via a call to {@link #setFactory setFactory} before the pool is used.
+ * </p>
+ * <p>
+ * Implementation note: To prevent possible deadlocks, care has been taken to
+ * ensure that no call to a factory method will occur within a synchronization
+ * block. See POOL-125 and DBCP-44 for more information.
+ * </p>
+ * 
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @see GenericObjectPool
+ * @author Rodney Waldhoff
+ * @author Dirk Verbeeck
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class GenericKeyedObjectPool<K, V> extends BaseKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
+
+    //--- 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 (per key) 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 (per key)
+     * from the pool.
+     * @see #getMaxActive
+     * @see #setMaxActive
+     */
+    public static final int DEFAULT_MAX_ACTIVE  = 8;
+
+    /**
+     * The default cap on the the overall maximum number of objects that can
+     * exist at one time.
+     * @see #getMaxTotal
+     * @see #setMaxTotal
+     */
+    public static final int DEFAULT_MAX_TOTAL  = -1;
+
+    /**
+     * 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 milliseconds) 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;
+
+    /**
+     * The default minimum level of idle objects in the pool.
+     * @since Pool 1.3
+     * @see #setMinIdle
+     * @see #getMinIdle
+     */
+    public static final int DEFAULT_MIN_IDLE = 0;
+
+    /**
+     * The default LIFO status. True means that borrowObject returns the
+     * most recently used ("last in") idle object in a pool (if there are
+     * idle instances available).  False means that pools behave as FIFO
+     * queues - objects are taken from idle object pools in the order that
+     * they are returned.
+     * @see #setLifo
+     */
+    public static final boolean DEFAULT_LIFO = true;
+
+    //--- constructors -----------------------------------------------
+
+    /**
+     * Create a new <code>GenericKeyedObjectPool</code> with no factory.
+     *
+     * @see #GenericKeyedObjectPool(KeyedPoolableObjectFactory)
+     * @see #setFactory(KeyedPoolableObjectFactory)
+     */
+    public GenericKeyedObjectPool() {
+        this(null, 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy
+     * objects if not <code>null</code>
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @param config a non-<code>null</code> {@link GenericKeyedObjectPool.Config} describing the configuration
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, GenericKeyedObjectPool.Config config) {
+        this(factory, config.maxActive, config.whenExhaustedAction, config.maxWait, config.maxIdle, config.maxTotal,
+                config.minIdle, config.testOnBorrow, config.testOnReturn, config.timeBetweenEvictionRunsMillis,
+                config.numTestsPerEvictionRun, config.minEvictableIdleTimeMillis, config.testWhileIdle, config.lifo);
+    }
+
+    /**
+     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @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 and
+     *  <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted and
+     * <code>whenExhaustedAction</code> 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<K, V> 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @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 and
+     * <code>whenExhaustedAction</code> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
+     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @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 and
+     * <code>whenExhaustedAction</code> 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<K, V> 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 <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @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 and
+     * <code>whenExhaustedAction</code> 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 eligible for eviction (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<K, V> factory, int maxActive, byte whenExhaustedAction,
+            long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
+            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,
+                testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
+                minEvictableIdleTimeMillis, testWhileIdle);
+    }
+
+    /**
+     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @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 and
+     * <code>whenExhaustedAction</code> 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 maxTotal the maximum number of objects that can exists at one time (see {@link #setMaxTotal})
+     * @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 eligible for eviction (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<K, V> factory, int maxActive, byte whenExhaustedAction,
+            long maxWait, int maxIdle, int maxTotal, boolean testOnBorrow, boolean testOnReturn,
+            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
+            boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal,
+                GenericKeyedObjectPool.DEFAULT_MIN_IDLE, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis,
+                numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle);
+    }
+
+    /**
+     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @param maxActive the maximum number of objects that can be borrowed 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 and
+     * <code>whenExhaustedAction</code> 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 maxTotal the maximum number of objects that can exists at one time (see {@link #setMaxTotal})
+     * @param minIdle the minimum number of idle objects to have in the pool at any one time (see {@link #setMinIdle})
+     * @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 eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
+     * (see {@link #setTestWhileIdle})
+     * @since Pool 1.3
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction,
+            long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn,
+            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
+            boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
+                DEFAULT_LIFO);
+    }
+
+    /**
+     * Create a new <code>GenericKeyedObjectPool</code> using the specified values.
+     * @param factory the <code>KeyedPoolableObjectFactory</code> to use to create, validate, and destroy objects
+     * if not <code>null</code>
+     * @param maxActive the maximum number of objects that can be borrowed 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 and
+     * <code>whenExhaustedAction</code> 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 maxTotal the maximum number of objects that can exists at one time (see {@link #setMaxTotal})
+     * @param minIdle the minimum number of idle objects to have in the pool at any one time (see {@link #setMinIdle})
+     * @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 eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
+     * (see {@link #setTestWhileIdle})
+     * @param lifo whether or not the pools behave as LIFO (last in first out) queues (see {@link #setLifo})
+     * @since Pool 1.4
+     */
+    public GenericKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction,
+            long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn,
+            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
+            boolean testWhileIdle, boolean lifo) {
+        _factory = factory;
+        _maxActive = maxActive;
+        _lifo = lifo;
+        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;
+        _maxTotal = maxTotal;
+        _minIdle = minIdle;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _testWhileIdle = testWhileIdle;
+
+        _poolMap = new HashMap<K, ObjectQueue>();
+        _poolList = new CursorableLinkedList<K>();
+
+        startEvictor(_timeBetweenEvictionRunsMillis);
+    }
+
+    //--- public methods ---------------------------------------------
+
+    //--- configuration methods --------------------------------------
+
+    /**
+     * Returns the cap on the number of object instances allocated by the pool
+     * (checked out or idle),  per key.
+     * A negative value indicates no limit.
+     *
+     * @return the cap on the number of active instances per key.
+     * @see #setMaxActive
+     */
+    public synchronized int getMaxActive() {
+        return _maxActive;
+    }
+
+    /**
+     * Sets the cap on the number of object instances managed by the pool per key.
+     * @param maxActive The cap on the number of object instances per key.
+     * Use a negative value for no limit.
+     *
+     * @see #getMaxActive
+     */
+    public void setMaxActive(int maxActive) {
+        synchronized(this) {
+            _maxActive = maxActive;
+        }
+        allocate();
+    }
+
+    /**
+     * Returns the overall maximum number of objects (across pools) that can
+     * exist at one time. A negative value indicates no limit.
+     * @return the maximum number of instances in circulation at one time.
+     * @see #setMaxTotal
+     */
+    public synchronized int getMaxTotal() {
+        return _maxTotal;
+    }
+
+    /**
+     * Sets the cap on the total number of instances from all pools combined.
+     * When <code>maxTotal</code> is set to a
+     * positive value and {@link #borrowObject borrowObject} is invoked
+     * when at the limit with no idle instances available, an attempt is made to
+     * create room by clearing the oldest 15% of the elements from the keyed
+     * pools.
+     *
+     * @param maxTotal The cap on the total number of instances across pools.
+     * Use a negative value for no limit.
+     * @see #getMaxTotal
+     */
+    public void setMaxTotal(int maxTotal) {
+        synchronized(this) {
+            _maxTotal = maxTotal;
+        }
+        allocate();
+    }
+
+    /**
+     * 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 synchronized 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 void setWhenExhaustedAction(byte whenExhaustedAction) {
+        synchronized(this) {
+            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.");
+            }
+        }
+        allocate();
+    }
+
+
+    /**
+     * 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 or equal to 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @return the maximum number of milliseconds borrowObject will block.
+     * @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 or equal to 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @param maxWait the maximum number of milliseconds borrowObject will block or negative for indefinitely.
+     * @see #getMaxWait
+     * @see #setWhenExhaustedAction
+     * @see #WHEN_EXHAUSTED_BLOCK
+     */
+    public void setMaxWait(long maxWait) {
+        synchronized(this) {
+            _maxWait = maxWait;
+        }
+        allocate();
+    }
+
+    /**
+     * Returns the cap on the number of "idle" instances per key.
+     * @return the maximum number of "idle" instances that can be held
+     * in a given keyed pool.
+     * @see #setMaxIdle
+     */
+    public synchronized int getMaxIdle() {
+        return _maxIdle;
+    }
+
+    /**
+     * Sets the cap on the number of "idle" instances in the pool.
+     * If maxIdle is set too low on heavily loaded systems it is possible you
+     * will see objects being destroyed and almost immediately new objects
+     * being created. This is a result of the active threads momentarily
+     * returning objects faster than they are requesting them them, causing the
+     * number of idle objects to rise above maxIdle. The best value for maxIdle
+     * for heavily loaded system will vary but the default is a good starting
+     * point.
+     * @param maxIdle the maximum number of "idle" instances that can be held
+     * in a given keyed pool. Use a negative value for no limit.
+     * @see #getMaxIdle
+     * @see #DEFAULT_MAX_IDLE
+     */
+    public void setMaxIdle(int maxIdle) {
+        synchronized(this) {
+            _maxIdle = maxIdle;
+        }
+        allocate();
+    }
+
+    /**
+     * Sets the minimum number of idle objects to maintain in each of the keyed
+     * pools. This setting has no effect unless
+     * <code>timeBetweenEvictionRunsMillis > 0</code> and attempts to ensure
+     * that each pool has the required minimum number of instances are only
+     * made during idle object eviction runs.
+     * @param poolSize - The minimum size of the each keyed pool
+     * @since Pool 1.3
+     * @see #getMinIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public void setMinIdle(int poolSize) {
+        _minIdle = poolSize;
+    }
+
+    /**
+     * Returns the minimum number of idle objects to maintain in each of the keyed
+     * pools. This setting has no effect unless
+     * <code>timeBetweenEvictionRunsMillis > 0</code> and attempts to ensure
+     * that each pool has the required minimum number of instances are only
+     * made during idle object eviction runs.
+     * @return minimum size of the each keyed pool
+     * @since Pool 1.3
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public int getMinIdle() {
+        return _minIdle;
+    }
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.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.
+     *
+     * @return <code>true</code> if objects are validated before being borrowed.
+     * @see #setTestOnBorrow
+     */
+    public boolean getTestOnBorrow() {
+        return _testOnBorrow;
+    }
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.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.
+     *
+     * @param testOnBorrow whether object should be validated before being returned by borrowObject.
+     * @see #getTestOnBorrow
+     */
+    public void setTestOnBorrow(boolean testOnBorrow) {
+        _testOnBorrow = testOnBorrow;
+    }
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @return <code>true</code> when objects will be validated before being returned.
+     * @see #setTestOnReturn
+     */
+    public boolean getTestOnReturn() {
+        return _testOnReturn;
+    }
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @param testOnReturn <code>true</code> so objects will be validated before being returned.
+     * @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.
+     *
+     * @return milliseconds to sleep between evictor runs.
+     * @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.
+     *
+     * @param timeBetweenEvictionRunsMillis milliseconds to sleep between evictor runs.
+     * @see #getTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        startEvictor(_timeBetweenEvictionRunsMillis);
+    }
+
+    /**
+     * Returns the max number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     *
+     * @return number of objects to examine each eviction run.
+     * @see #setNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized int getNumTestsPerEvictionRun() {
+        return _numTestsPerEvictionRun;
+    }
+
+    /**
+     * Sets the max number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     * <p>
+     * When a negative value is supplied, 
+     * <code>ceil({@link #getNumIdle()})/abs({@link #getNumTestsPerEvictionRun})</code>
+     * tests will be run.  I.e., when the value is <code>-n</code>, roughly one <code>n</code>th of the
+     * idle objects will be tested per run.  When the value is positive, the number of tests
+     * actually performed in each run will be the minimum of this value and the number of instances
+     * idle in the pools.
+     *
+     * @param numTestsPerEvictionRun number of objects to examine each eviction run.
+     * @see #setNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+    }
+
+    /**
+     * Returns the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor
+     * (if any).
+     *
+     * @return minimum amount of time an object may sit idle in the pool before it is eligible for eviction.
+     * @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 eligible 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.
+     *
+     * @param minEvictableIdleTimeMillis minimum amount of time an object may sit idle in the pool before
+     * it is eligible for eviction.
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @return <code>true</code> when objects are validated when borrowed.
+     * @see #setTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized boolean getTestWhileIdle() {
+        return _testWhileIdle;
+    }
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated}
+     * by the idle object evictor (if any).  If an object
+     * fails to validate, it will be dropped from the pool.
+     *
+     * @param testWhileIdle <code>true</code> so objects are validated when borrowed.
+     * @see #getTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTestWhileIdle(boolean testWhileIdle) {
+        _testWhileIdle = testWhileIdle;
+    }
+
+    /**
+     * Sets the configuration.
+     * @param conf the new configuration to use.
+     * @see GenericKeyedObjectPool.Config
+     */
+    public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {
+        setMaxIdle(conf.maxIdle);
+        setMaxActive(conf.maxActive);
+        setMaxTotal(conf.maxTotal);
+        setMinIdle(conf.minIdle);
+        setMaxWait(conf.maxWait);
+        setWhenExhaustedAction(conf.whenExhaustedAction);
+        setTestOnBorrow(conf.testOnBorrow);
+        setTestOnReturn(conf.testOnReturn);
+        setTestWhileIdle(conf.testWhileIdle);
+        setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
+        setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
+        setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
+    }
+
+    /**
+     * Whether or not the idle object pools act as LIFO queues. True means
+     * that borrowObject returns the most recently used ("last in") idle object
+     * in a pool (if there are idle instances available).  False means that
+     * the pools behave as FIFO queues - objects are taken from idle object
+     * pools in the order that they are returned.
+     *
+     * @return <code>true</code> if the pools are configured to act as LIFO queues
+     * @since 1.4
+     */
+     public synchronized boolean getLifo() {
+         return _lifo;
+     }
+
+     /**
+      * Sets the LIFO property of the pools. True means that borrowObject returns
+      * the most recently used ("last in") idle object in a pool (if there are
+      * idle instances available).  False means that the pools behave as FIFO
+      * queues - objects are taken from idle object pools in the order that
+      * they are returned.
+      *
+      * @param lifo the new value for the lifo property
+      * @since 1.4
+      */
+     public synchronized void setLifo(boolean lifo) {
+         this._lifo = lifo;
+     }
+
+    //-- ObjectPool methods ------------------------------------------
+
+    /**
+     * <p>Borrows an object from the keyed pool associated with the given key.</p>
+     * 
+     * <p>If there is an idle instance available in the pool associated with the given key, then
+     * either the most-recently returned (if {@link #getLifo() lifo} == true) or "oldest" (lifo == false)
+     * instance sitting idle in the pool will be activated and returned.  If activation fails, or
+     * {@link #getTestOnBorrow() testOnBorrow} is set to true and validation fails, the instance is destroyed and the
+     * next available instance is examined.  This continues until either a valid instance is returned or there
+     * are no more idle instances available.</p>
+     * 
+     * <p>If there are no idle instances available in the pool associated with the given key, behavior
+     * depends on the {@link #getMaxActive() maxActive}, {@link #getMaxTotal() maxTotal}, and (if applicable)
+     * {@link #getWhenExhaustedAction() whenExhaustedAction} and {@link #getMaxWait() maxWait} properties. If the
+     * number of instances checked out from the pool under the given key is less than <code>maxActive</code> and
+     * the total number of instances in circulation (under all keys) is less than <code>maxTotal</code>, a new instance
+     * is created, activated and (if applicable) validated and returned to the caller.</p>
+     * 
+     * <p>If the associated keyed pool is exhausted (no available idle instances and no capacity to create new ones),
+     * this method will either block ({@link #WHEN_EXHAUSTED_BLOCK}), throw a <code>NoSuchElementException</code>
+     * ({@link #WHEN_EXHAUSTED_FAIL}), or grow ({@link #WHEN_EXHAUSTED_GROW} - ignoring maxActive, maxTotal properties).
+     * The length of time that this method will block when <code>whenExhaustedAction == WHEN_EXHAUSTED_BLOCK</code>
+     * is determined by the {@link #getMaxWait() maxWait} property.</p>
+     * 
+     * <p>When the pool is exhausted, multiple calling threads may be simultaneously blocked waiting for instances
+     * to become available.  As of pool 1.5, a "fairness" algorithm has been implemented to ensure that threads receive
+     * available instances in request arrival order.</p>
+     * 
+     * @param key pool key
+     * @return object instance from the keyed pool
+     * @throws NoSuchElementException if a keyed object instance cannot be returned.
+     */
+     @Override
+    public V borrowObject(K key) throws Exception {
+        long starttime = System.currentTimeMillis();
+        Latch<K, V> latch = new Latch<K, V>(key);
+        byte whenExhaustedAction;
+        long maxWait;
+        synchronized (this) {
+            // Get local copy of current config. Can't sync when used later as
+            // it can result in a deadlock. Has the added advantage that config
+            // is consistent for entire method execution
+            whenExhaustedAction = _whenExhaustedAction;
+            maxWait = _maxWait;
+
+            // Add this request to the queue
+            _allocationQueue.add(latch);
+        }
+        // Work the allocation queue, allocating idle instances and
+        // instance creation permits in request arrival order
+        allocate();
+
+        for(;;) {
+            synchronized (this) {
+                assertOpen();
+            }
+            // If no object was allocated
+            if (null == latch.getPair()) {
+                // Check to see if we were allowed to create one
+                if (latch.mayCreate()) {
+                    // allow new object to be created
+                } else {
+                    // the pool is exhausted
+                    switch(whenExhaustedAction) {
+                        case WHEN_EXHAUSTED_GROW:
+                            // allow new object to be created
+                            synchronized (this) {
+                                // Make sure another thread didn't allocate us an object
+                                // or permit a new object to be created
+                                if (latch.getPair() == null && !latch.mayCreate()) {
+                                    _allocationQueue.remove(latch);
+                                    latch.getPool().incrementInternalProcessingCount();
+                                }
+                            }
+                        break;
+                        case WHEN_EXHAUSTED_FAIL:
+                            synchronized (this) {
+                                // Make sure allocate hasn't already assigned an object
+                                // in a different thread or permitted a new object to be created
+                                if (latch.getPair() != null || latch.mayCreate()) {
+                                    break;
+                                }
+                                _allocationQueue.remove(latch);
+                            }
+                            throw new NoSuchElementException("Pool exhausted");
+                        case WHEN_EXHAUSTED_BLOCK:
+                            try {
+                                synchronized (latch) {
+                                    // Before we wait, make sure another thread didn't allocate us an object
+                                    // or permit a new object to be created
+                                    if (latch.getPair() == null && !latch.mayCreate()) {
+                                        if (maxWait <= 0) {
+                                            latch.wait();
+                                        } else {
+                                            // this code may be executed again after a notify then continue cycle
+                                            // so, need to calculate the amount of time to wait
+                                            final long elapsed = (System.currentTimeMillis() - starttime);
+                                            final long waitTime = maxWait - elapsed;
+                                            if (waitTime > 0)
+                                            {
+                                                latch.wait(waitTime);
+                                            }
+                                        }
+                                    } else {
+                                        break;
+                                    }
+                                }
+                                // see if we were awakened by a closing pool
+                                if(isClosed() == true) {
+                                    throw new IllegalStateException("Pool closed");
+                                }
+                            } catch(InterruptedException e) {
+                                boolean doAllocate = false;
+                                synchronized (this) {
+                                    // Need to handle the all three possibilities
+                                    if (latch.getPair() == null && !latch.mayCreate()) {
+                                        // Case 1: latch still in allocation queue
+                                        // Remove latch from the allocation queue
+                                        _allocationQueue.remove(latch);
+                                    } else if (latch.getPair() == null && latch.mayCreate()) {
+                                        // Case 2: latch has been given permission to create
+                                        //         a new object
+                                        latch.getPool().decrementInternalProcessingCount();
+                                        doAllocate = true;
+                                    } else {
+                                        // Case 3: An object has been allocated
+                                        latch.getPool().decrementInternalProcessingCount();
+                                        latch.getPool().incrementActiveCount();
+                                        returnObject(latch.getkey(), latch.getPair().getValue());
+                                    }
+                                }
+                                if (doAllocate) {
+                                    allocate();
+                                }
+                                Thread.currentThread().interrupt();
+                                throw e;
+                            }
+                            if (maxWait > 0 && ((System.currentTimeMillis() - starttime) >= maxWait)) {
+                                synchronized (this) {
+                                    // Make sure allocate hasn't already assigned an object
+                                    // in a different thread or permitted a new object to be created
+                                    if (latch.getPair() == null && !latch.mayCreate()) {
+                                        _allocationQueue.remove(latch);
+                                    } else {
+                                        break;
+                                    }
+                                }
+                                throw new NoSuchElementException("Timeout waiting for idle object");
+                            } else {
+                                continue; // keep looping
+                            }
+                        default:
+                            throw new IllegalArgumentException("whenExhaustedAction " + whenExhaustedAction +
+                                    " not recognized.");
+                    }
+                }
+            }
+
+            boolean newlyCreated = false;
+            if (null == latch.getPair()) {
+                try {
+                    V obj = _factory.makeObject(key);
+                    latch.setPair(new ObjectTimestampPair<V>(obj));
+                    newlyCreated = true;
+                } finally {
+                    if (!newlyCreated) {
+                        // object cannot be created
+                        synchronized (this) {
+                            latch.getPool().decrementInternalProcessingCount();
+                            // No need to reset latch - about to throw exception
+                        }
+                        allocate();
+                    }
+                }
+            }
+
+            // activate & validate the object
+            try {
+                _factory.activateObject(key, latch.getPair().value);
+                if (_testOnBorrow && !_factory.validateObject(key, latch.getPair().value)) {
+                    throw new Exception("ValidateObject failed");
+                }
+                synchronized (this) {
+                    latch.getPool().decrementInternalProcessingCount();
+                    latch.getPool().incrementActiveCount();
+                }
+                return latch.getPair().value;
+            } catch (Throwable e) {
+                PoolUtils.checkRethrow(e);
+                // object cannot be activated or is invalid
+                try {
+                    _factory.destroyObject(key, latch.getPair().value);
+                } catch (Throwable e2) {
+                    PoolUtils.checkRethrow(e2);
+                    // cannot destroy broken object
+                }
+                synchronized (this) {
+                    latch.getPool().decrementInternalProcessingCount();
+                    if (!newlyCreated) {
+                        latch.reset();
+                        _allocationQueue.add(0, latch);
+                    }
+                }
+                allocate();
+                if (newlyCreated) {
+                    throw new NoSuchElementException(
+                       "Could not create a validated object, cause: " +
+                            e.getMessage());
+                }
+                else {
+                    continue; // keep looping
+                }
+            }
+        }
+    }
+
+    /**
+     * Allocate available instances to latches in the allocation queue.  Then
+     * set _mayCreate to true for as many additional latches remaining in queue
+     * as _maxActive allows for each key. This method <b>MUST NOT</b> be called
+     * from inside a sync block.
+     */
+    private void allocate() {
+        boolean clearOldest = false;
+
+        synchronized (this) {
+            if (isClosed()) return;
+            
+            Iterator<Latch<K, V>> allocationQueueIter = _allocationQueue.iterator();
+            
+            while (allocationQueueIter.hasNext()) {
+                // First use any objects in the pool to clear the queue
+                Latch<K, V> latch = allocationQueueIter.next();
+                ObjectQueue pool = (_poolMap.get(latch.getkey()));
+                if (null == pool) {
+                    pool = new ObjectQueue();
+                    _poolMap.put(latch.getkey(), pool);
+                    _poolList.add(latch.getkey());
+                }
+                latch.setPool(pool);
+                if (!pool.queue.isEmpty()) {
+                    allocationQueueIter.remove();
+                    latch.setPair(
+                            pool.queue.removeFirst());
+                    pool.incrementInternalProcessingCount();
+                    _totalIdle--;
+                    synchronized (latch) {
+                        latch.notify();
+                    }
+                    // Next item in queue
+                    continue;
+                }
+
+                // If there is a totalMaxActive and we are at the limit then
+                // we have to make room
+                if ((_maxTotal > 0) &&
+                        (_totalActive + _totalIdle + _totalInternalProcessing >= _maxTotal)) {
+                    clearOldest = true;
+                    break;
+                }
+
+                // Second utilise any spare capacity to create new objects
+                if ((_maxActive < 0 || pool.activeCount + pool.internalProcessingCount < _maxActive) &&
+                        (_maxTotal < 0 || _totalActive + _totalIdle + _totalInternalProcessing < _maxTotal)) {
+                    // allow new object to be created
+                    allocationQueueIter.remove();
+                    latch.setMayCreate(true);
+                    pool.incrementInternalProcessingCount();
+                    synchronized (latch) {
+                        latch.notify();
+                    }
+                    // Next item in queue
+                    continue;
+                }
+
+                // If there is no per-key limit and we reach this point we
+                // must have allocated all the objects we possibly can and there
+                // is no point looking at the rest of the allocation queue
+                if (_maxActive < 0) {
+                    break;
+                }
+            }
+        }
+        
+        if (clearOldest) {
+            /* Clear oldest calls factory methods so it must be called from
+             * outside the sync block.
+             * It also needs to be outside the sync block as it calls
+             * allocate(). If called inside the sync block, the call to
+             * allocate() would be able to enter the sync block (since the
+             * thread already has the lock) which may have unexpected,
+             * unpleasant results.
+             */
+            clearOldest();
+        }
+    }
+    
+    /**
+     * Clears any objects sitting idle in the pool by removing them from the
+     * idle instance pool and then invoking the configured PoolableObjectFactory's
+     * {@link KeyedPoolableObjectFactory#destroyObject(Object, Object)} method on
+     * each idle instance.
+     *  
+     * <p> Implementation notes:
+     * <ul><li>This method does not destroy or effect in any way instances that are
+     * checked out when it is invoked.</li>
+     * <li>Invoking this method does not prevent objects being
+     * returned to the idle instance pool, even during its execution. It locks
+     * the pool only during instance removal. Additional instances may be returned
+     * while removed items are being destroyed.</li>
+     * <li>Exceptions encountered destroying idle instances are swallowed.</li></ul></p>
+     */
+    @Override
+    public void clear() {
+        Map<K,  List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K,  List<ObjectTimestampPair<V>>>();
+        synchronized (this) {
+            for (Iterator<K> it = _poolMap.keySet().iterator(); it.hasNext();) {
+                K key = it.next();
+                ObjectQueue pool = _poolMap.get(key);
+                // Copy objects to new list so pool.queue can be cleared inside
+                // the sync
+                List<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>();
+                objects.addAll(pool.queue);
+                toDestroy.put(key, objects);
+                it.remove();
+                _poolList.remove(key);
+                _totalIdle = _totalIdle - pool.queue.size();
+                _totalInternalProcessing =
+                    _totalInternalProcessing + pool.queue.size();
+                pool.queue.clear();
+            }
+        }
+        destroy(toDestroy, _factory);
+    }
+
+    /**
+     * Clears oldest 15% of objects in pool.  The method sorts the
+     * objects into a TreeMap and then iterates the first 15% for removal.
+     * 
+     * @since Pool 1.3
+     */
+    public void clearOldest() {
+        // Map of objects to destroy my key
+        final Map<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>();
+
+        // build sorted map of idle objects
+        final Map<ObjectTimestampPair<V>, K> map = new TreeMap<ObjectTimestampPair<V>, K>();
+        synchronized (this) {
+            for (Iterator<K> keyiter = _poolMap.keySet().iterator(); keyiter.hasNext();) {
+                final K key = keyiter.next();
+                final List<ObjectTimestampPair<V>> list = _poolMap.get(key).queue;
+                for (Iterator<ObjectTimestampPair<V>> it = list.iterator(); it.hasNext();) {
+                    // each item into the map uses the objectimestamppair object
+                    // as the key.  It then gets sorted based on the timstamp field
+                    // each value in the map is the parent list it belongs in.
+                    map.put(it.next(), key);
+                }
+            }
+
+            // Now iterate created map and kill the first 15% plus one to account for zero
+            Set<Entry<ObjectTimestampPair<V>, K>> setPairKeys = map.entrySet();
+            int itemsToRemove = ((int) (map.size() * 0.15)) + 1;
+
+            Iterator<Entry<ObjectTimestampPair<V>, K>> iter = setPairKeys.iterator();
+            while (iter.hasNext() && itemsToRemove > 0) {
+                Entry<ObjectTimestampPair<V>, K> entry = iter.next();
+                // kind of backwards on naming.  In the map, each key is the objecttimestamppair
+                // because it has the ordering with the timestamp value.  Each value that the
+                // key references is the key of the list it belongs to.
+                K key = entry.getValue();
+                ObjectTimestampPair<V> pairTimeStamp = entry.getKey();
+                ObjectQueue objectQueue = _poolMap.get(key);
+                final List<ObjectTimestampPair<V>> list = objectQueue.queue;
+                list.remove(pairTimeStamp);
+
+                if (toDestroy.containsKey(key)) {
+                    toDestroy.get(key).add(pairTimeStamp);
+                } else {
+                    List<ObjectTimestampPair<V>> listForKey = new ArrayList<ObjectTimestampPair<V>>();
+                    listForKey.add(pairTimeStamp);
+                    toDestroy.put(key, listForKey);
+                }
+                objectQueue.incrementInternalProcessingCount();
+                _totalIdle--;
+                itemsToRemove--;
+            }
+
+        }
+        destroy(toDestroy, _factory);
+    }
+
+    /**
+     * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
+     *
+     * @param key the key to clear
+     */
+    @Override
+    public void clear(K key) {
+        Map<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K , List<ObjectTimestampPair<V>>>();
+
+        final ObjectQueue pool;
+        synchronized (this) {
+            pool = _poolMap.remove(key);
+            if (pool == null) {
+                return;
+            } else {
+                _poolList.remove(key);
+            }
+            // Copy objects to new list so pool.queue can be cleared inside
+            // the sync
+            List<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>();
+            objects.addAll(pool.queue);
+            toDestroy.put(key, objects);
+            _totalIdle = _totalIdle - pool.queue.size();
+            _totalInternalProcessing =
+                _totalInternalProcessing + pool.queue.size();
+            pool.queue.clear();
+        }
+        destroy(toDestroy, _factory);
+    }
+
+    /**
+     * Assuming Map<Object,Collection<ObjectTimestampPair>>, destroy all
+     * ObjectTimestampPair.value using the supplied factory.
+     * 
+     * @param m Map containing keyed pools to clear
+     * @param factory KeyedPoolableObjectFactory used to destroy the objects
+     */
+    private void destroy(Map<K,  List<ObjectTimestampPair<V>>> m, KeyedPoolableObjectFactory<K, V> factory) {
+        for (Iterator<Entry<K,  List<ObjectTimestampPair<V>>>> entries = m.entrySet().iterator(); entries.hasNext();) {
+            Entry<K,  List<ObjectTimestampPair<V>>> entry = entries.next();
+            K key = entry.getKey();
+            List<ObjectTimestampPair<V>> c = entry.getValue();
+            for (Iterator<ObjectTimestampPair<V>> it = c.iterator(); it.hasNext();) {
+                try {
+                    factory.destroyObject(
+                            key,it.next().value);
+                } catch(Exception e) {
+                    // ignore error, keep destroying the rest
+                } finally {
+                    synchronized(this) {
+                        ObjectQueue objectQueue =
+                                _poolMap.get(key);
+                        if (objectQueue != null) {
+                            objectQueue.decrementInternalProcessingCount();
+                            if (objectQueue.internalProcessingCount == 0 &&
+                                    objectQueue.activeCount == 0 &&
+                                    objectQueue.queue.isEmpty()) {
+                                _poolMap.remove(key);
+                                _poolList.remove(key);
+                            }
+                        } else {
+                            _totalInternalProcessing--;
+                        }
+                    }
+                    allocate();
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Returns the total number of instances current borrowed from this pool but not yet returned.
+     *
+     * @return the total number of instances currently borrowed from this pool
+     */
+    @Override
+    public synchronized int getNumActive() {
+        return _totalActive;
+    }
+
+    /**
+     * Returns the total number of instances currently idle in this pool.
+     *
+     * @return the total number of instances currently idle in this pool
+     */
+    @Override
+    public synchronized int getNumIdle() {
+        return _totalIdle;
+    }
+
+    /**
+     * Returns the number of instances currently borrowed from but not yet returned
+     * to the pool corresponding to the given <code>key</code>.
+     *
+     * @param key the key to query
+     * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
+     */
+    @Override
+    public synchronized int getNumActive(Object key) {
+        final ObjectQueue pool = (_poolMap.get(key));
+        return pool != null ? pool.activeCount : 0;
+    }
+
+    /**
+     * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
+     *
+     * @param key the key to query
+     * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
+     */
+    @Override
+    public synchronized int getNumIdle(Object key) {
+        final ObjectQueue pool = (_poolMap.get(key));
+        return pool != null ? pool.queue.size() : 0;
+    }
+
+    /**
+     * <p>Returns an object to a keyed pool.</p>
+     * 
+     * <p>For the pool to function correctly, the object instance <strong>must</strong> have been borrowed
+     * from the pool (under the same key) and not yet returned. Repeated <code>returnObject</code> calls on
+     * the same object/key pair (with no <code>borrowObject</code> calls in between) will result in multiple
+     * references to the object in the idle instance pool.</p>
+     * 
+     * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances under the given
+     * key has reached this value, the returning instance is destroyed.</p>
+     * 
+     * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned
+     * to the idle instance pool under the given key.  In this case, if validation fails, the instance is destroyed.</p>
+     * 
+     * @param key pool key
+     * @param obj instance to return to the keyed pool
+     * @throws Exception
+     */
+    @Override
+    public void returnObject(K key, V obj) throws Exception {
+        try {
+            addObjectToPool(key, obj, true);
+        } catch (Exception e) {
+            if (_factory != null) {
+                try {
+                    _factory.destroyObject(key, obj);
+                } catch (Exception e2) {
+                    // swallowed
+                }
+                // TODO: Correctness here depends on control in addObjectToPool.
+                // These two methods should be refactored, removing the
+                // "behavior flag", decrementNumActive, from addObjectToPool.
+                ObjectQueue pool = (_poolMap.get(key));
+                if (pool != null) {
+                    synchronized(this) {
+                        pool.decrementActiveCount();
+                        if (pool.queue.isEmpty() &&
+                                pool.activeCount == 0 &&
+                                pool.internalProcessingCount == 0) {
+                            _poolMap.remove(key);
+                            _poolList.remove(key);
+                        }
+                    }
+                    allocate();
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>Adds an object to the keyed pool.</p>
+     * 
+     * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool.
+     * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance
+     * is destroyed.</p>
+     * 
+     * <p>Calls {@link #allocate()} on successful completion</p>
+     * 
+     * @param key pool key
+     * @param obj instance to add to the keyed pool
+     * @param decrementNumActive whether or not to decrement the active count associated with the keyed pool
+     * @throws Exception
+     */
+    private void addObjectToPool(K key, V obj,
+            boolean decrementNumActive) throws Exception {
+
+        // if we need to validate this object, do so
+        boolean success = true; // whether or not this object passed validation
+        if (_testOnReturn && !_factory.validateObject(key, obj)) {
+            success = false;
+        } else {
+            _factory.passivateObject(key, obj);
+        }
+
+        boolean shouldDestroy = !success;
+        ObjectQueue pool;
+
+        // Add instance to pool if there is room and it has passed validation
+        // (if testOnreturn is set)
+        boolean doAllocate = false;
+        synchronized (this) {
+            // grab the pool (list) of objects associated with the given key
+            pool = _poolMap.get(key);
+            // if it doesn't exist, create it
+            if (null == pool) {
+                pool = new ObjectQueue();
+                _poolMap.put(key, pool);
+                _poolList.add(key);
+            }
+            if (isClosed()) {
+                shouldDestroy = true;
+            } else {
+                // if there's no space in the pool, flag the object for destruction
+                // else if we passivated successfully, return it to the pool
+                if (_maxIdle >= 0 && (pool.queue.size() >= _maxIdle)) {
+                    shouldDestroy = true;
+                } else if (success) {
+                    // borrowObject always takes the first element from the queue,
+                    // so for LIFO, push on top, FIFO add to end
+                    if (_lifo) {
+                        pool.queue.addFirst(new ObjectTimestampPair<V>(obj));
+                    } else {
+                        pool.queue.addLast(new ObjectTimestampPair<V>(obj));
+                    }
+                    _totalIdle++;
+                    if (decrementNumActive) {
+                        pool.decrementActiveCount();
+                    }
+                    doAllocate = true;
+                }
+            }
+        }
+        if (doAllocate) {
+            allocate();
+        }
+
+        // Destroy the instance if necessary
+        if (shouldDestroy) {
+            try {
+                _factory.destroyObject(key, obj);
+            } catch(Exception e) {
+                // ignored?
+            }
+            // Decrement active count *after* destroy if applicable
+            if (decrementNumActive) {
+                synchronized(this) {
+                    pool.decrementActiveCount();
+                    if (pool.queue.isEmpty() &&
+                            pool.activeCount == 0 &&
+                            pool.internalProcessingCount == 0) {
+                        _poolMap.remove(key);
+                        _poolList.remove(key);
+                    }
+                }
+                allocate();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>Activation of this method decrements the active count associated with the given keyed pool 
+     * and attempts to destroy <code>obj.</code></p>
+     * 
+     * @param key pool key
+     * @param obj instance to invalidate
+     * @throws Exception if an exception occurs destroying the object
+     */
+    @Override
+    public void invalidateObject(K key, V obj) throws Exception {
+        try {
+            _factory.destroyObject(key, obj);
+        } finally {
+            synchronized (this) {
+                ObjectQueue pool = (_poolMap.get(key));
+                if (null == pool) {
+                    pool = new ObjectQueue();
+                    _poolMap.put(key, pool);
+                    _poolList.add(key);
+                }
+                pool.decrementActiveCount();
+            }
+            allocate(); // _totalActive has changed
+        }
+    }
+
+    /**
+     * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
+     * passivate it, and then place it in the idle object pool.
+     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
+     *
+     * @param key the key a new instance should be added to
+     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
+     * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been
+     * called on this pool.
+     */
+    @Override
+    public void addObject(K key) throws Exception {
+        assertOpen();
+        if (_factory == null) {
+            throw new IllegalStateException("Cannot add objects without a factory.");
+        }
+        V obj = _factory.makeObject(key);
+        try {
+            assertOpen();
+            addObjectToPool(key, obj, false);
+        } catch (IllegalStateException ex) { // Pool closed
+            try {
+                _factory.destroyObject(key, obj);
+            } catch (Exception ex2) {
+                // swallow
+            }
+            throw ex;
+        }
+    }
+
+    /**
+     * Registers a key for pool control.
+     *
+     * If <code>populateImmediately</code> is <code>true</code> and
+     * <code>minIdle > 0,</code> the pool under the given key will be
+     * populated immediately with <code>minIdle</code> idle instances.
+     *
+     * @param key - The key to register for pool control.
+     * @param populateImmediately - If this is <code>true</code>, the pool
+     * will be populated immediately.
+     * @since Pool 1.3
+     */
+    public synchronized void preparePool(K key, boolean populateImmediately) {
+        ObjectQueue pool = (_poolMap.get(key));
+        if (null == pool) {
+            pool = new ObjectQueue();
+            _poolMap.put(key,pool);
+            _poolList.add(key);
+        }
+
+        if (populateImmediately) {
+            try {
+                // Create the pooled objects
+                ensureMinIdle(key);
+            }
+            catch (Exception e) {
+                //Do nothing
+            }
+        }
+    }
+
+    /**
+     * <p>Closes the keyed object pool.  Once the pool is closed, {@link #borrowObject(Object)}
+     * will fail with IllegalStateException, but {@link #returnObject(Object, Object)} and
+     * {@link #invalidateObject(Object, Object)} will continue to work, with returned objects
+     * destroyed on return.</p>
+     * 
+     * <p>Destroys idle instances in the pool by invoking {@link #clear()}.</p> 
+     * 
+     * @throws Exception
+     */
+    @Override
+    public void close() throws Exception {
+        super.close();
+        synchronized (this) {
+            clear();
+            if (null != _evictionCursor) {
+                _evictionCursor.close();
+                _evictionCursor = null;
+            }
+            if (null != _evictionKeyCursor) {
+                _evictionKeyCursor.close();
+                _evictionKeyCursor = null;
+            }
+            startEvictor(-1L);
+            
+            while(_allocationQueue.size() > 0) {
+                Latch<K, V> l = _allocationQueue.removeFirst();
+                
+                synchronized (l) {
+                    // notify the waiting thread
+                    l.notify();
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>Sets the keyed poolable object factory associated with this pool.</p>
+     * 
+     * <p>If this method is called when objects are checked out of any of the keyed pools,
+     * an IllegalStateException is thrown.  Calling this method also has the side effect of
+     * destroying any idle instances in existing keyed pools, using the original factory.</p>
+     * 
+     * @param factory KeyedPoolableObjectFactory to use when creating keyed object pool instances
+     * @throws IllegalStateException if there are active (checked out) instances associated with this keyed object pool
+     * @deprecated to be removed in version 2.0
+     */
+    @Deprecated
+    @Override
+    public void setFactory(KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException {
+        Map<K, List<ObjectTimestampPair<V>>> toDestroy = new HashMap<K, List<ObjectTimestampPair<V>>>();
+        final KeyedPoolableObjectFactory<K, V> oldFactory = _factory;
+        synchronized (this) {
+            assertOpen();
+            if (0 < getNumActive()) {
+                throw new IllegalStateException("Objects are already active");
+            } else {
+                for (Iterator<K> it = _poolMap.keySet().iterator(); it.hasNext();) {
+                    K key = it.next();
+                    ObjectQueue pool = _poolMap.get(key);
+                    if (pool != null) {
+                        // Copy objects to new list so pool.queue can be cleared
+                        // inside the sync
+                        List<ObjectTimestampPair<V>> objects = new ArrayList<ObjectTimestampPair<V>>();
+                        objects.addAll(pool.queue);
+                        toDestroy.put(key, objects);
+                        it.remove();
+                        _poolList.remove(key);
+                        _totalIdle = _totalIdle - pool.queue.size();
+                        _totalInternalProcessing =
+                            _totalInternalProcessing + pool.queue.size();
+                        pool.queue.clear();
+                    }
+                }
+                _factory = factory;
+            }
+        }
+        destroy(toDestroy, oldFactory);
+    }
+
+    /**
+     * <p>Perform <code>numTests</code> idle object eviction tests, evicting
+     * examined objects that meet the criteria for eviction. If
+     * <code>testWhileIdle</code> is true, examined objects are validated
+     * when visited (and removed if invalid); otherwise only objects that
+     * have been idle for more than <code>minEvicableIdletimeMillis</code>
+     * are removed.</p>
+     *
+     * <p>Successive activations of this method examine objects in keyed pools
+     * in sequence, cycling through the keys and examining objects in
+     * oldest-to-youngest order within the keyed pools.</p>
+     *
+     * @throws Exception when there is a problem evicting idle objects.
+     */
+    public void evict() throws Exception {
+        K key = null;
+        boolean testWhileIdle;
+        long minEvictableIdleTimeMillis;
+
+        synchronized (this) {
+            // Get local copy of current config. Can't sync when used later as
+            // it can result in a deadlock. Has the added advantage that config
+            // is consistent for entire method execution
+            testWhileIdle = _testWhileIdle;
+            minEvictableIdleTimeMillis = _minEvictableIdleTimeMillis;
+
+            // Initialize key to last key value
+            if (_evictionKeyCursor != null &&
+                    _evictionKeyCursor._lastReturned != null) {
+                key = _evictionKeyCursor._lastReturned.value();
+            }
+        }
+
+        for (int i=0, m=getNumTests(); i<m; i++) {
+            final ObjectTimestampPair<V> pair;
+            synchronized (this) {
+                // make sure pool map is not empty; otherwise do nothing
+                if (_poolMap == null || _poolMap.size() == 0) {
+                    continue;
+                }
+
+                // if we don't have a key cursor, then create one
+                if (null == _evictionKeyCursor) {
+                    resetEvictionKeyCursor();
+                    key = null;
+                }
+
+                // if we don't have an object cursor, create one
+                if (null == _evictionCursor) {
+                    // if the _evictionKeyCursor has a next value, use this key
+                    if (_evictionKeyCursor.hasNext()) {
+                        key = _evictionKeyCursor.next();
+                        resetEvictionObjectCursor(key);
+                    } else {
+                        // Reset the key cursor and try again
+                        resetEvictionKeyCursor();
+                        if (_evictionKeyCursor != null) {
+                            if (_evictionKeyCursor.hasNext()) {
+                                key = _evictionKeyCursor.next();
+                                resetEvictionObjectCursor(key);
+                            }
+                        }
+                    }
+                }
+
+                if (_evictionCursor == null) {
+                    continue; // should never happen; do nothing
+                }
+
+                // If eviction cursor is exhausted, try to move
+                // to the next key and reset
+                if ((_lifo && !_evictionCursor.hasPrevious()) ||
+                        (!_lifo && !_evictionCursor.hasNext())) {
+                    if (_evictionKeyCursor != null) {
+                        if (_evictionKeyCursor.hasNext()) {
+                            key = _evictionKeyCursor.next();
+                            resetEvictionObjectCursor(key);
+                        } else { // Need to reset Key cursor
+                            resetEvictionKeyCursor();
+                            if (_evictionKeyCursor != null) {
+                                if (_evictionKeyCursor.hasNext()) {
+                                    key = _evictionKeyCursor.next();
+                                    resetEvictionObjectCursor(key);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if ((_lifo && !_evictionCursor.hasPrevious()) ||
+                        (!_lifo && !_evictionCursor.hasNext())) {
+                    continue; // reset failed, do nothing
+                }
+
+                // if LIFO and the _evictionCursor has a previous object,
+                // or FIFO and _evictionCursor has a next object, test it
+                pair = _lifo ?
+                        _evictionCursor.previous() :
+                        _evictionCursor.next();
+                _evictionCursor.remove();
+                ObjectQueue objectQueue = _poolMap.get(key);
+                objectQueue.incrementInternalProcessingCount();
+                _totalIdle--;
+            }
+
+            boolean removeObject=false;
+            if ((minEvictableIdleTimeMillis > 0) &&
+               (System.currentTimeMillis() - pair.tstamp >
+               minEvictableIdleTimeMillis)) {
+                removeObject=true;
+            }
+            if (testWhileIdle && removeObject == false) {
+                boolean active = false;
+                try {
+                    _factory.activateObject(key,pair.value);
+                    active = true;
+                } catch(Exception e) {
+                    removeObject=true;
+                }
+                if (active) {
+                    if (!_factory.validateObject(key,pair.value)) {
+                        removeObject=true;
+                    } else {
+                        try {
+                            _factory.passivateObject(key,pair.value);
+                        } catch(Exception e) {
+                            removeObject=true;
+                        }
+                    }
+                }
+            }
+
+            if (removeObject) {
+                try {
+                    _factory.destroyObject(key, pair.value);
+                } catch(Exception e) {
+                    // ignored
+                }
+            }
+            synchronized (this) {
+                ObjectQueue objectQueue =
+                    _poolMap.get(key);
+                objectQueue.decrementInternalProcessingCount();
+                if (removeObject) {
+                    if (objectQueue.queue.isEmpty() &&
+                            objectQueue.activeCount == 0 &&
+                            objectQueue.internalProcessingCount == 0) {
+                        _poolMap.remove(key);
+                        _poolList.remove(key);
+                    }
+                } else {
+                    _evictionCursor.add(pair);
+                    _totalIdle++;
+                    if (_lifo) {
+                        // Skip over the element we just added back
+                        _evictionCursor.previous();
+                    }
+                }
+            }
+        }
+        allocate();
+    }
+
+    /**
+     * Resets the eviction key cursor and closes any
+     * associated eviction object cursor
+     */
+    private void resetEvictionKeyCursor() {
+        if (_evictionKeyCursor != null) {
+            _evictionKeyCursor.close();
+        }
+        _evictionKeyCursor = _poolList.cursor();
+        if (null != _evictionCursor) {
+            _evictionCursor.close();
+            _evictionCursor = null;
+        }
+    }
+
+    /**
+     * Resets the eviction object cursor for the given key
+     *
+     * @param key eviction key
+     */
+    private void resetEvictionObjectCursor(Object key) {
+        if (_evictionCursor != null) {
+            _evictionCursor.close();
+        }
+        if (_poolMap == null) {
+            return;
+        }
+        ObjectQueue pool = _poolMap.get(key);
+        if (pool != null) {
+            CursorableLinkedList<ObjectTimestampPair<V>> queue = pool.queue;
+            _evictionCursor = queue.cursor(_lifo ? queue.size() : 0);
+        }
+    }
+
+    /**
+     * Iterates through all the known keys and creates any necessary objects to maintain
+     * the minimum level of pooled objects.
+     * @see #getMinIdle
+     * @see #setMinIdle
+     * @throws Exception If there was an error whilst creating the pooled objects.
+     */
+    @SuppressWarnings("unchecked")
+    private void ensureMinIdle() throws Exception {
+        //Check if should sustain the pool
+        if (_minIdle > 0) {
+            Object[] keysCopy;
+            synchronized(this) {
+                // Get the current set of keys
+                keysCopy = _poolMap.keySet().toArray();
+            }
+
+            // Loop through all elements in _poolList
+            // Find out the total number of max active and max idle for that class
+            // If the number is less than the minIdle, do creation loop to boost numbers
+            for (int i=0; i < keysCopy.length; i++) {
+                //Get the next key to process
+                ensureMinIdle((K)keysCopy[i]);
+            }
+        }
+    }
+
+    /**
+     * Re-creates any needed objects to maintain the minimum levels of
+     * pooled objects for the specified key.
+     *
+     * This method uses {@link #calculateDeficit} to calculate the number
+     * of objects to be created. {@link #calculateDeficit} can be overridden to
+     * provide a different method of calculating the number of objects to be
+     * created.
+     * @param key The key to process
+     * @throws Exception If there was an error whilst creating the pooled objects
+     */
+    private void ensureMinIdle(K key) throws Exception {
+        // Calculate current pool objects
+        ObjectQueue pool;
+        synchronized(this) {
+            pool = (_poolMap.get(key));
+        }
+        if (pool == null) {
+            return;
+        }
+
+        // this method isn't synchronized so the
+        // calculateDeficit is done at the beginning
+        // as a loop limit and a second time inside the loop
+        // to stop when another thread already returned the
+        // needed objects
+        int objectDeficit = calculateDeficit(pool, false);
+
+        for (int i = 0; i < objectDeficit && calculateDeficit(pool, true) > 0; i++) {
+            try {
+                addObject(key);
+            } finally {
+                synchronized (this) {
+                    pool.decrementInternalProcessingCount();
+                }
+                allocate();
+            }
+        }
+    }
+
+    //--- non-public methods ----------------------------------------
+
+    /**
+     * Start the eviction thread or service, or when
+     * <code>delay</code> is non-positive, stop it
+     * if it is already running.
+     *
+     * @param delay milliseconds between evictor runs.
+     */
+    protected synchronized void startEvictor(long delay) {
+        if (null != _evictor) {
+            EvictionTimer.cancel(_evictor);
+            _evictor = null;
+        }
+        if (delay > 0) {
+            _evictor = new Evictor();
+            EvictionTimer.schedule(_evictor, delay, delay);
+        }
+    }
+
+    /**
+     * Returns pool info including {@link #getNumActive()}, {@link #getNumIdle()}
+     * and currently defined keys.
+     * 
+     * @return string containing debug information
+     */
+    synchronized String debugInfo() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Active: ").append(getNumActive()).append("\n");
+        buf.append("Idle: ").append(getNumIdle()).append("\n");
+        Iterator<K> it = _poolMap.keySet().iterator();
+        while (it.hasNext()) {
+            K key = it.next();
+            buf.append("\t").append(key).append(" ").append(_poolMap.get(key)).append("\n");
+        }
+        return buf.toString();
+    }
+
+    /** 
+     * Returns the number of tests to be performed in an Evictor run,
+     * based on the current values of <code>_numTestsPerEvictionRun</code>
+     * and <code>_totalIdle</code>.
+     * 
+     * @see #setNumTestsPerEvictionRun
+     * @return the number of tests for the Evictor to run
+     */
+    private synchronized int getNumTests() {
+        if (_numTestsPerEvictionRun >= 0) {
+            return Math.min(_numTestsPerEvictionRun, _totalIdle);
+        } else {
+            return(int)(Math.ceil(_totalIdle/Math.abs((double)_numTestsPerEvictionRun)));
+        }
+    }
+
+    /**
+     * This returns the number of objects to create during the pool
+     * sustain cycle. This will ensure that the minimum number of idle
+     * instances is maintained without going past the maxActive value.
+     * 
+     * @param pool the ObjectPool to calculate the deficit for
+     * @param incrementInternal - Should the count of objects currently under
+     *                            some form of internal processing be
+     *                            incremented?
+     * @return The number of objects to be created
+     */
+    private synchronized int calculateDeficit(ObjectQueue pool,
+            boolean incrementInternal) {
+        int objectDefecit = 0;
+
+        //Calculate no of objects needed to be created, in order to have
+        //the number of pooled objects < maxActive();
+        objectDefecit = getMinIdle() - pool.queue.size();
+        if (getMaxActive() > 0) {
+            int growLimit = Math.max(0, getMaxActive() - pool.activeCount - pool.queue.size() - pool.internalProcessingCount);
+            objectDefecit = Math.min(objectDefecit, growLimit);
+        }
+
+        // Take the maxTotal limit into account
+        if (getMaxTotal() > 0) {
+            int growLimit = Math.max(0, getMaxTotal() - getNumActive() - getNumIdle() - _totalInternalProcessing);
+            objectDefecit = Math.min(objectDefecit, growLimit);
+        }
+
+        if (incrementInternal && objectDefecit > 0) {
+            pool.incrementInternalProcessingCount();
+        }
+        return objectDefecit;
+    }
+
+    //--- inner classes ----------------------------------------------
+
+    /**
+     * A "struct" that keeps additional information about the actual queue of pooled objects.
+     */
+    private class ObjectQueue {
+        /** Number of instances checked out to clients from this queue */
+        private int activeCount = 0;
+        
+        /** Idle instance queue */
+        private final CursorableLinkedList<ObjectTimestampPair<V>> queue = new CursorableLinkedList<ObjectTimestampPair<V>>();
+        
+        /** Number of instances in process of being created */
+        private int internalProcessingCount = 0;
+
+        /** Increment the active count for this queue */
+        void incrementActiveCount() {
+            synchronized (GenericKeyedObjectPool.this) {
+                _totalActive++;
+            }
+            activeCount++;
+        }
+
+        /** Decrement the active count for this queue */
+        void decrementActiveCount() {
+            synchronized (GenericKeyedObjectPool.this) {
+                _totalActive--;
+            }
+            if (activeCount > 0) {
+                activeCount--;
+            }
+        }
+
+        /** Record the fact that one more instance is queued for creation */
+        void incrementInternalProcessingCount() {
+            synchronized (GenericKeyedObjectPool.this) {
+                _totalInternalProcessing++;
+            }
+            internalProcessingCount++;
+        }
+
+        /** Decrement the number of instances in process of being created */
+        void decrementInternalProcessingCount() {
+            synchronized (GenericKeyedObjectPool.this) {
+                _totalInternalProcessing--;
+            }
+            internalProcessingCount--;
+        }
+    }
+
+    /**
+     * A simple "struct" encapsulating an object instance and a timestamp.
+     *
+     * Implements Comparable, objects are sorted from old to new.
+     *
+     * This is also used by {@link GenericObjectPool}.
+     */
+    static class ObjectTimestampPair<T> implements Comparable<T> {
+        //CHECKSTYLE: stop VisibilityModifier
+        /** 
+         * Object instance 
+         * @deprecated this field will be made private and final in version 2.0
+         */
+        @Deprecated
+        T value;
+        
+        /**
+         * timestamp
+         * @deprecated this field will be made private and final in version 2.0
+         */
+        @Deprecated
+        long tstamp;
+        //CHECKSTYLE: resume VisibilityModifier
+
+        /**
+         * Create a new ObjectTimestampPair using the given object and the current system time.
+         * @param val object instance
+         */
+        ObjectTimestampPair(T val) {
+            this(val, System.currentTimeMillis());
+        }
+
+        /**
+         * Create a new ObjectTimeStampPair using the given object and timestamp value.
+         * @param val object instance
+         * @param time long representation of timestamp
+         */
+        ObjectTimestampPair(T val, long time) {
+            value = val;
+            tstamp = time;
+        }
+
+        /**
+         * Returns a string representation.
+         * 
+         * @return String representing this ObjectTimestampPair
+         */
+        @Override
+        public String toString() {
+            return value + ";" + tstamp;
+        }
+
+        /**
+         * Compares this to another object by casting the argument to an
+         * ObjectTimestampPair.
+         * 
+         * @param obj object to cmpare
+         * @return result of comparison
+         */
+        @SuppressWarnings("unchecked")
+        public int compareTo(Object obj) {
+            return compareTo((ObjectTimestampPair<T>) obj);
+        }
+
+        /**
+         * Compares this to another ObjectTimestampPair, using the timestamp as basis for comparison.
+         * Implementation is consistent with equals.
+         * 
+         * @param other object to compare
+         * @return result of comparison
+         */
+        public int compareTo(ObjectTimestampPair<T> other) {
+            final long tstampdiff = this.tstamp - other.tstamp;
+            if (tstampdiff == 0) {
+                // make sure the natural ordering is consistent with equals
+                // see java.lang.Comparable Javadocs
+                return System.identityHashCode(this) - System.identityHashCode(other);
+            } else {
+                // handle int overflow
+                return (int)Math.min(Math.max(tstampdiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
+            }
+        }
+
+        /**
+         * @return the value
+         */
+        public T getValue() {
+            return value;
+        }
+
+        /**
+         * @return the tstamp
+         */
+        public long getTstamp() {
+            return tstamp;
+        }
+    }
+
+    /**
+     * The idle object evictor {@link TimerTask}.
+     * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
+     */
+    private class Evictor extends TimerTask {
+        /**
+         * Run pool maintenance.  Evict objects qualifying for eviction and then
+         * invoke {@link GenericKeyedObjectPool#ensureMinIdle()}.
+         */
+        @Override
+        public void run() {
+            //Evict from the pool
+            try {
+                evict();
+            } catch(Exception e) {
+                // ignored
+            } catch(OutOfMemoryError oome) {
+                // Log problem but give evictor thread a chance to continue in
+                // case error is recoverable
+                oome.printStackTrace(System.err);
+            }
+            //Re-create idle instances.
+            try {
+                ensureMinIdle();
+            } catch (Exception e) {
+                // ignored
+            }
+        }
+    }
+
+    /**
+     * A simple "struct" encapsulating the
+     * configuration information for a <code>GenericKeyedObjectPool</code>.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory,GenericKeyedObjectPool.Config)
+     * @see GenericKeyedObjectPool#setConfig
+     */
+    public static class Config {
+        //CHECKSTYLE: stop VisibilityModifier
+        /**
+         * @see GenericKeyedObjectPool#setMaxIdle
+         */
+        public int maxIdle = GenericKeyedObjectPool.DEFAULT_MAX_IDLE;
+        /**
+         * @see GenericKeyedObjectPool#setMaxActive
+         */
+        public int maxActive = GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE;
+        /**
+         * @see GenericKeyedObjectPool#setMaxTotal
+         */
+        public int maxTotal = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
+        /**
+         * @see GenericKeyedObjectPool#setMinIdle
+         */
+        public int minIdle = GenericKeyedObjectPool.DEFAULT_MIN_IDLE;
+        /**
+         * @see GenericKeyedObjectPool#setMaxWait
+         */
+        public long maxWait = GenericKeyedObjectPool.DEFAULT_MAX_WAIT;
+        /**
+         * @see GenericKeyedObjectPool#setWhenExhaustedAction
+         */
+        public byte whenExhaustedAction = GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+        /**
+         * @see GenericKeyedObjectPool#setTestOnBorrow
+         */
+        public boolean testOnBorrow = GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW;
+        /**
+         * @see GenericKeyedObjectPool#setTestOnReturn
+         */
+        public boolean testOnReturn = GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN;
+        /**
+         * @see GenericKeyedObjectPool#setTestWhileIdle
+         */
+        public boolean testWhileIdle = GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE;
+        /**
+         * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
+         */
+        public long timeBetweenEvictionRunsMillis = GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+        /**
+         * @see GenericKeyedObjectPool#setNumTestsPerEvictionRun
+         */
+        public int numTestsPerEvictionRun =  GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+        /**
+         * @see GenericKeyedObjectPool#setMinEvictableIdleTimeMillis
+         */
+        public long minEvictableIdleTimeMillis = GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+        /**
+         * @see GenericKeyedObjectPool#setLifo
+         */
+        public boolean lifo = GenericKeyedObjectPool.DEFAULT_LIFO;
+        //CHECKSTYLE: resume VisibilityModifier
+    }
+
+    /**
+     * Latch used to control allocation order of objects to threads to ensure
+     * fairness. That is, for each key, objects are allocated to threads in the order
+     * that threads request objects.
+     * 
+     * @since 1.5
+     */
+    private final class Latch<LK, LV> {
+        
+        /** key of associated pool */
+        private final LK _key;
+        
+        /** keyed pool associated with this latch */
+        private ObjectQueue _pool;
+        
+        /** holds an ObjectTimestampPair when this latch has been allocated an instance */
+        private ObjectTimestampPair<LV> _pair;
+        
+        /** indicates that this latch can create an instance */
+        private boolean _mayCreate = false;
+
+        /**
+         * Create a latch with the given key
+         * @param key key of the pool associated with this latch
+         */
+        private Latch(LK key) {
+            _key = key;
+        }
+
+        /**
+         * Retuns the key of the associated pool
+         * @return associated pool key
+         */
+        private synchronized LK getkey() {
+            return _key;
+        }
+
+        /**
+         * Returns the pool associated with this latch
+         * @return pool
+         */
+        private synchronized ObjectQueue getPool() {
+            return _pool;
+        }
+        
+        /**
+         * Sets the pool associated with this latch
+         * @param pool the pool
+         */
+        private synchronized void setPool(ObjectQueue pool) {
+            _pool = pool;
+        }
+
+        /**
+         * Gets the ObjectTimestampPair allocated to this latch.
+         * Returns null if this latch does not have an instance allocated to it. 
+         * @return the associated ObjectTimestampPair
+         */
+        private synchronized ObjectTimestampPair<LV> getPair() {
+            return _pair;
+        }
+        
+        /**
+         * Allocate an ObjectTimestampPair to this latch.
+         * @param pair ObjectTimestampPair on this latch
+         */
+        private synchronized void setPair(ObjectTimestampPair<LV> pair) {
+            _pair = pair;
+        }
+
+        /**
+         * Whether or not this latch can create an instance
+         * @return true if this latch has an instance creation permit
+         */
+        private synchronized boolean mayCreate() {
+            return _mayCreate;
+        }
+        
+        /**
+         * Sets the mayCreate property
+         * 
+         * @param mayCreate true means this latch can create an instance
+         */
+        private synchronized void setMayCreate(boolean mayCreate) {
+            _mayCreate = mayCreate;
+        }
+
+        /**
+         * Reset the latch data. Used when an allocation fails and the latch
+         * needs to be re-added to the queue.
+         */
+        private synchronized void reset() {
+            _pair = null;
+            _mayCreate = false;
+        }
+    }
+
+    //--- protected attributes ---------------------------------------
+
+    /**
+     * The cap on the number of idle instances in the pool.
+     * @see #setMaxIdle
+     * @see #getMaxIdle
+     */
+    private int _maxIdle = DEFAULT_MAX_IDLE;
+
+    /**
+     * The minimum no of idle objects to keep in the pool.
+     * @see #setMinIdle
+     * @see #getMinIdle
+     */
+    private volatile int _minIdle = DEFAULT_MIN_IDLE;
+
+    /**
+     * The cap on the number of active instances from the pool.
+     * @see #setMaxActive
+     * @see #getMaxActive
+     */
+    private int _maxActive = DEFAULT_MAX_ACTIVE;
+
+    /**
+     * The cap on the total number of instances from the pool if non-positive.
+     * @see #setMaxTotal
+     * @see #getMaxTotal
+     */
+    private int _maxTotal = DEFAULT_MAX_TOTAL;
+
+    /**
+     * 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 or equal to 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #setMaxWait
+     * @see #getMaxWait
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #setWhenExhaustedAction
+     * @see #getWhenExhaustedAction
+     */
+    private 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
+     */
+    private byte _whenExhaustedAction = DEFAULT_WHEN_EXHAUSTED_ACTION;
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.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
+     */
+    private volatile boolean _testOnBorrow = DEFAULT_TEST_ON_BORROW;
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.PoolableObjectFactory#validateObject validated}
+     * before being returned to the pool within the
+     * {@link #returnObject}.
+     *
+     * @see #getTestOnReturn
+     * @see #setTestOnReturn
+     */
+    private volatile boolean _testOnReturn = DEFAULT_TEST_ON_RETURN;
+
+    /**
+     * When <code>true</code>, objects will be
+     * {@link org.apache.commons.pool.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
+     */
+    private 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
+     */
+    private 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, <code>ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})</code>
+     * tests will be run.  I.e., when the value is <code>-n</code>, roughly one <code>n</code>th of the
+     * idle objects will be tested per run.
+     *
+     * @see #setNumTestsPerEvictionRun
+     * @see #getNumTestsPerEvictionRun
+     * @see #getTimeBetweenEvictionRunsMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    private int _numTestsPerEvictionRun =  DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+
+    /**
+     * The minimum amount of time an object may sit idle in the pool
+     * before it is eligible 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
+     */
+    private long _minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+    /** My hash of pools (ObjectQueue). */
+    private Map<K, ObjectQueue> _poolMap = null;
+
+    /** The total number of active instances. */
+    private int _totalActive = 0;
+
+    /** The total number of idle instances. */
+    private int _totalIdle = 0;
+
+    /**
+     * The number of objects subject to some form of internal processing
+     * (usually creation or destruction) that should be included in the total
+     * number of objects but are neither active nor idle.
+     */
+    private int _totalInternalProcessing = 0;
+
+    /** My {@link KeyedPoolableObjectFactory}. */
+    private KeyedPoolableObjectFactory<K, V> _factory = null;
+
+    /**
+     * My idle object eviction {@link TimerTask}, if any.
+     */
+    private Evictor _evictor = null;
+
+    /**
+     * A cursorable list of my pools.
+     * @see GenericKeyedObjectPool.Evictor#run
+     */
+    private CursorableLinkedList<K> _poolList = null;
+
+    /** Eviction cursor (over instances within-key) */
+    private CursorableLinkedList<ObjectTimestampPair<V>>.Cursor _evictionCursor = null;
+    
+    /** Eviction cursor (over keys) */
+    private CursorableLinkedList<K>.Cursor _evictionKeyCursor = null;
+
+    /** Whether or not the pools behave as LIFO queues (last in first out) */
+    private boolean _lifo = DEFAULT_LIFO;
+
+    /**
+     * Used to track the order in which threads call {@link #borrowObject()} so
+     * that objects can be allocated in the order in which the threads requested
+     * them.
+     */
+    private LinkedList<Latch<K, V>> _allocationQueue = new LinkedList<Latch<K, V>>();
+
+}
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..0f650cb
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPoolFactory.java
@@ -0,0 +1,473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedObjectPoolFactory;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+
+/**
+ * A factory for creating {@link GenericKeyedObjectPool} instances.
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @see GenericKeyedObjectPool
+ * @see KeyedObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @author Dirk Verbeeck
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class GenericKeyedObjectPoolFactory<K, V> implements KeyedObjectPoolFactory<K, V> {
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> 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,GenericKeyedObjectP [...]
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param config a non-null GenericKeyedObjectPool.Config describing the configuration.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, GenericKeyedObjectPool.Config)
+     * @throws NullPointerException when config is <code>null</code>.
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, GenericKeyedObjectPool.Config config) throws NullPointerException {
+        this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.maxTotal,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.lifo);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive) {
+        this(factory,maxActive,GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericKeyedObjectPool.DEFAULT_MAX_WAIT,GenericKeyedObjectPool.DEFAULT_MAX_IDLE, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,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,GenericKe [...]
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericKeyedObjectPool.DEFAULT_MAX_IDLE, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,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);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param testOnBorrow whether to validate objects before they are returned by borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to returnObject.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, boolean, boolean)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericKeyedObjectPool.DEFAULT_MAX_IDLE, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,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);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, int)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,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);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @param maxTotal the maximum number of objects that can exists at one time.
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int maxTotal) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle, maxTotal, 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);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @param testOnBorrow whether to validate objects before they are returned by borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to returnObject.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, int, boolean, boolean)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL,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);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @param testOnBorrow whether to validate objects before they are returned by borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run of the evictor.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether to validate objects in the idle object eviction thread.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, int, boolean, boolean, long, int, long, boolean)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @param maxTotal the maximum number of objects that can exists at one time.
+     * @param testOnBorrow whether to validate objects before they are returned by borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run of the evictor.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether to validate objects in the idle object eviction thread.
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, int, int, boolean, boolean, long, int, long, boolean)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int maxTotal, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal, GenericKeyedObjectPool.DEFAULT_MIN_IDLE , testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @param maxTotal the maximum number of objects that can exists at one time.
+     * @param minIdle the minimum number of idle objects to have in the pool at any one time.
+     * @param testOnBorrow whether to validate objects before they are returned by borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run of the evictor.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether to validate objects in the idle object eviction thread.
+     * @since Pool 1.3
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, int, int, int, boolean, boolean, long, int, long, boolean)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, GenericKeyedObjectPool.DEFAULT_LIFO);
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory to used by created pools.
+     * @param maxActive the maximum number of objects that can be borrowed from pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in the pools.
+     * @param maxTotal the maximum number of objects that can exists at one time.
+     * @param minIdle the minimum number of idle objects to have in the pool at any one time.
+     * @param testOnBorrow whether to validate objects before they are returned by borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run of the evictor.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether to validate objects in the idle object eviction thread.
+     * @param lifo whether or not objects are returned in last-in-first-out order from the idle object pool.
+     * @since Pool 1.4
+     * @see GenericKeyedObjectPool#GenericKeyedObjectPool(KeyedPoolableObjectFactory, int, byte, long, int, int, int, boolean, boolean, long, int, long, boolean, boolean)
+     */
+    public GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int maxTotal, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, boolean lifo) {
+        _maxIdle = maxIdle;
+        _maxActive = maxActive;
+        _maxTotal = maxTotal;
+        _minIdle = minIdle;
+        _maxWait = maxWait;
+        _whenExhaustedAction = whenExhaustedAction;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _testWhileIdle = testWhileIdle;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _factory = factory;
+        _lifo = lifo;
+    }
+
+    /**
+     * Create a new GenericKeyedObjectPool with the currently configured properties.
+     * 
+     * @return GenericKeyedObjectPool with {@link GenericKeyedObjectPool.Config Configuration} determined by
+     * current property settings
+     */
+    public KeyedObjectPool<K, V> createPool() {
+        return new GenericKeyedObjectPool<K, V>(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_maxTotal,_minIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle,_lifo);
+    }
+    
+    /**
+     * @return the {@link GenericKeyedObjectPool#getMaxIdle() maxIdle} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMaxIdle() {
+        return _maxIdle;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getMaxActive() maxActive} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMaxActive() {
+        return _maxActive;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getMaxTotal() maxTotal} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMaxTotal() {
+        return _maxTotal;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getMinIdle() minIdle} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMinIdle() {
+        return _minIdle;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getMaxWait() maxWait} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getMaxWait() {
+        return _maxWait;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getWhenExhaustedAction() whenExhaustedAction} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public byte getWhenExhaustedAction() {
+        return _whenExhaustedAction;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getTestOnBorrow() testOnBorrow} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getTestOnBorrow() {
+        return _testOnBorrow;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getTestOnReturn() testOnReturn} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getTestOnReturn() {
+        return _testOnReturn;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getTestWhileIdle() testWhileIdle} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getTestWhileIdle() {
+        return _testWhileIdle;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getTimeBetweenEvictionRunsMillis() {
+        return _timeBetweenEvictionRunsMillis;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getNumTestsPerEvictionRun() numTestsPerEvictionRun}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getNumTestsPerEvictionRun() {
+        return _numTestsPerEvictionRun;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() minEvictableIdleTimeMillis}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getMinEvictableIdleTimeMillis() {
+        return _minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * @return the {@link KeyedPoolableObjectFactory} used by pools created by this factory.
+     * @since 1.5.5
+     */
+    public KeyedPoolableObjectFactory<K, V> getFactory() {
+        return _factory;
+    }
+
+    /**
+     * @return the {@link GenericKeyedObjectPool#getLifo() lifo} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getLifo() {
+        return _lifo;
+    }
+
+    //--- protected attributes - deprecated, use getters to access these properties
+
+    /**
+     * The {@link GenericKeyedObjectPool#getMaxIdle() maxIdle} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getMaxIdle()}.
+     */
+    @Deprecated
+    protected int _maxIdle = GenericKeyedObjectPool.DEFAULT_MAX_IDLE;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getMaxActive() maxActive} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getMaxActive()}.
+     */
+    @Deprecated
+    protected int _maxActive = GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getMaxTotal() maxTotal} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getMaxTotal()}.
+     */
+    @Deprecated
+    protected int _maxTotal = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getMinIdle() minIdle} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getMinIdle()}.
+     */
+    @Deprecated
+    protected int _minIdle = GenericKeyedObjectPool.DEFAULT_MIN_IDLE;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getMaxWait() maxWait} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getMaxWait()}.
+     */
+    @Deprecated
+    protected long _maxWait = GenericKeyedObjectPool.DEFAULT_MAX_WAIT;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getWhenExhaustedAction() whenExhaustedAction} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getWhenExhaustedAction()}.
+     */
+    @Deprecated
+    protected byte _whenExhaustedAction = GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getTestOnBorrow() testOnBorrow} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getTestOnBorrow()}.
+     */
+    @Deprecated
+    protected boolean _testOnBorrow = GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getTestOnReturn() testOnReturn} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getTestOnReturn()}.
+     */
+    @Deprecated
+    protected boolean _testOnReturn = GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getTestWhileIdle() testWhileIdle} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getTestWhileIdle()}.
+     */
+    @Deprecated
+    protected boolean _testWhileIdle = GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis} setting for
+     * pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getTimeBetweenEvictionRunsMillis()}.
+     */
+    @Deprecated
+    protected long _timeBetweenEvictionRunsMillis = GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getNumTestsPerEvictionRun() numTestsPerEvictionRun} setting for
+     * pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getNumTestsPerEvictionRun()}.
+     */
+    @Deprecated
+    protected int _numTestsPerEvictionRun =  GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getMinEvictableIdleTimeMillis() minEvictableIdleTimeMillis} setting for
+     * pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getMinEvictableIdleTimeMillis()}.
+     */
+    @Deprecated
+    protected long _minEvictableIdleTimeMillis = GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    
+    /**
+     * The {@link KeyedPoolableObjectFactory} used by pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getFactory()}.
+     */
+    @Deprecated
+    protected KeyedPoolableObjectFactory<K, V> _factory = null;
+    
+    /**
+     * The {@link GenericKeyedObjectPool#getLifo() lifo} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0. Use {@link #getLifo()}.
+     */
+    @Deprecated
+    protected boolean _lifo = GenericKeyedObjectPool.DEFAULT_LIFO;
+
+}
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..8968719
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericObjectPool.java
@@ -0,0 +1,2093 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.TimerTask;
+
+import org.apache.commons.pool.BaseObjectPool;
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolUtils;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.impl.GenericKeyedObjectPool.ObjectTimestampPair;
+
+/**
+ * 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 allocated by the pool (checked out to clients, or
+ *    idle awaiting checkout) at a given time.  When non-positive, there is no
+ *    limit to the number of objects that can be managed by the pool at one time.
+ *    When {@link #setMaxActive <i>maxActive</i>} is reached, the pool is said
+ *    to be exhausted. The default setting for this parameter is 8.
+ *  </li>
+ *  <li>
+ *    {@link #setMaxIdle <i>maxIdle</i>} controls the maximum number of objects
+ *    that can sit idle in the pool at any time.  When negative, there is no
+ *    limit to the number of objects that may be idle at one time. The default
+ *    setting for this parameter is 8.
+ *  </li>
+ *  <li>
+ *    {@link #setWhenExhaustedAction <i>whenExhaustedAction</i>} specifies the
+ *    behavior 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, then {@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>
+ *    The default <code>whenExhaustedAction</code> setting is
+ *    {@link #WHEN_EXHAUSTED_BLOCK} and the default <code>maxWait</code>
+ *    setting is -1. By default, therefore, <code>borrowObject</code> will
+ *    block indefinitely until an idle instance becomes available.
+ *  </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. The default setting for this parameter is
+ *    <code>false.</code>
+ *  </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.
+ *    The default setting for this parameter is <code>false.</code>
+ *  </li>
+ * </ul>
+ * <p>
+ * Optionally, one may configure the pool to examine and possibly evict objects
+ * as they sit idle in the pool and to ensure that a minimum number of idle
+ * objects are available. This is performed by an "idle object eviction"
+ * thread, which runs asynchronously. Caution should be used when configuring
+ * this optional feature. Eviction runs contend with client threads for access
+ * to objects in the pool, so if they run too frequently performance issues may
+ * result. 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. The
+ *   default setting for this parameter is -1 (i.e., idle object eviction is
+ *   disabled by default).
+ *  </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 eligible for eviction due to idle time.  When non-positive, no object
+ *   will be dropped from the pool due to idle time alone. This setting has no
+ *   effect unless <code>timeBetweenEvictionRunsMillis > 0.</code> The default
+ *   setting for this parameter is 30 minutes.
+ *  </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. This setting has no effect unless
+ *   <code>timeBetweenEvictionRunsMillis > 0.</code>  The default setting for
+ *   this parameter is <code>false.</code>
+ *  </li>
+ *  <li>
+ *   {@link #setSoftMinEvictableIdleTimeMillis <i>softMinEvictableIdleTimeMillis</i>}
+ *   specifies the minimum amount of time an object may sit idle in the pool
+ *   before it is eligible for eviction by the idle object evictor
+ *   (if any), with the extra condition that at least "minIdle" object instances
+ *   remain in the pool.  When non-positive, no objects will be evicted from the pool
+ *   due to idle time alone. This setting has no effect unless
+ *   <code>timeBetweenEvictionRunsMillis > 0.</code> and it is superceded by
+ *   {@link #setMinEvictableIdleTimeMillis <i>minEvictableIdleTimeMillis</i>}
+ *   (that is, if <code>minEvictableIdleTimeMillis</code> is positive, then
+ *   <code>softMinEvictableIdleTimeMillis</code> is ignored). The default setting for
+ *   this parameter is -1 (disabled).
+ *  </li>
+ *  <li>
+ *   {@link #setNumTestsPerEvictionRun <i>numTestsPerEvictionRun</i>}
+ *   determines the number of objects examined in each run of the idle object
+ *   evictor. This setting has no effect unless
+ *   <code>timeBetweenEvictionRunsMillis > 0.</code>  The default setting for
+ *   this parameter is 3.
+ *  </li>
+ * </ul>
+ * <p>
+ * <p>
+ * The pool can be configured to behave as a LIFO queue with respect to idle
+ * objects - always returning the most recently used object from the pool,
+ * or as a FIFO queue, where borrowObject always returns the oldest object
+ * in the idle object pool.
+ * <ul>
+ *  <li>
+ *   {@link #setLifo <i>lifo</i>}
+ *   determines whether or not the pool returns idle objects in
+ *   last-in-first-out order. The default setting for this parameter is
+ *   <code>true.</code>
+ *  </li>
+ * </ul>
+ * <p>
+ * GenericObjectPool is not usable without a {@link PoolableObjectFactory}.  A
+ * non-<code>null</code> factory must be provided either as a constructor argument
+ * or via a call to {@link #setFactory} before the pool is used.
+ * <p>
+ * Implementation note: To prevent possible deadlocks, care has been taken to
+ * ensure that no call to a factory method will occur within a synchronization
+ * block. See POOL-125 and DBCP-44 for more information.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @see GenericKeyedObjectPool
+ * @author Rodney Waldhoff
+ * @author Dirk Verbeeck
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class GenericObjectPool<T> extends BaseObjectPool<T> implements ObjectPool<T> {
+
+    //--- 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 minimum number of "sleeping" instances in the pool
+     * before before the evictor thread (if active) spawns new objects.
+     * @see #getMinIdle
+     * @see #setMinIdle
+     */
+    public static final int DEFAULT_MIN_IDLE = 0;
+
+    /**
+     * 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 LIFO status. True means that borrowObject returns the
+     * most recently used ("last in") idle object in the pool (if there are
+     * idle instances available).  False means that the pool behaves as a FIFO
+     * queue - objects are taken from the idle object pool in the order that
+     * they are returned to the pool.
+     * @see #setLifo
+     * @since 1.4
+     */
+    public static final boolean DEFAULT_LIFO = true;
+
+    /**
+     * The default maximum amount of time (in milliseconds) 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;
+
+    /**
+     * The default value for {@link #getSoftMinEvictableIdleTimeMillis}.
+     * @see #getSoftMinEvictableIdleTimeMillis
+     * @see #setSoftMinEvictableIdleTimeMillis
+     */
+    public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1;
+
+    //--- constructors -----------------------------------------------
+
+    /**
+     * Create a new <tt>GenericObjectPool</tt> with default properties.
+     */
+    public GenericObjectPool() {
+        this(null, DEFAULT_MAX_ACTIVE, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
+                DEFAULT_MIN_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 factory.
+     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
+     */
+    public GenericObjectPool(PoolableObjectFactory<T> factory) {
+        this(factory, DEFAULT_MAX_ACTIVE, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE,
+                DEFAULT_MIN_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<T> factory, GenericObjectPool.Config config) {
+        this(factory, config.maxActive, config.whenExhaustedAction, config.maxWait, config.maxIdle, config.minIdle,
+                config.testOnBorrow, config.testOnReturn, config.timeBetweenEvictionRunsMillis, 
+                config.numTestsPerEvictionRun, config.minEvictableIdleTimeMillis, config.testWhileIdle, 
+                config.softMinEvictableIdleTimeMillis, config.lifo);
+    }
+
+    /**
+     * 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<T> factory, int maxActive) {
+        this(factory, maxActive, DEFAULT_WHEN_EXHAUSTED_ACTION, DEFAULT_MAX_WAIT, DEFAULT_MAX_IDLE, DEFAULT_MIN_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<T> factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, DEFAULT_MAX_IDLE, DEFAULT_MIN_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 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<T> factory, int maxActive, byte whenExhaustedAction, long maxWait,
+            boolean testOnBorrow, boolean testOnReturn) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, DEFAULT_MAX_IDLE, DEFAULT_MIN_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 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 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<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_MIN_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 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 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<T> factory, int maxActive, byte whenExhaustedAction, long maxWait,
+            int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_MIN_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 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 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 eligible for eviction (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<T> factory, int maxActive, byte whenExhaustedAction, long maxWait,
+            int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
+            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_MIN_IDLE, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, 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 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 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 minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
+     * @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 eligible for eviction (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<T> factory, int maxActive, byte whenExhaustedAction, long maxWait,
+            int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
+            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
+                DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+    }
+
+    /**
+     * 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 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 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 minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
+     * @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 eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
+     * (see {@link #setTestWhileIdle})
+     * @param softMinEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is
+     * eligible for eviction with the extra condition that at least "minIdle" amount of object remain in the pool.
+     * (see {@link #setSoftMinEvictableIdleTimeMillis})
+     * @since Pool 1.3
+     */
+    public GenericObjectPool(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait,
+            int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
+            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle,
+            long softMinEvictableIdleTimeMillis) {
+        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
+                softMinEvictableIdleTimeMillis, DEFAULT_LIFO);
+    }
+
+    /**
+     * 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 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 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 minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
+     * @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 eligible for eviction (see {@link #setMinEvictableIdleTimeMillis})
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any
+     * (see {@link #setTestWhileIdle})
+     * @param softMinEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the
+     * pool before it is eligible for eviction with the extra condition that at least "minIdle" amount of object
+     * remain in the pool. (see {@link #setSoftMinEvictableIdleTimeMillis})
+     * @param lifo whether or not objects are returned in last-in-first-out order from the idle object pool
+     * (see {@link #setLifo})
+     * @since Pool 1.4
+     */
+    public GenericObjectPool(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait,
+            int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis,
+            int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle,
+            long softMinEvictableIdleTimeMillis, boolean lifo) {
+        _factory = factory;
+        _maxActive = maxActive;
+        _lifo = lifo;
+        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;
+        _minIdle = minIdle;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
+        _testWhileIdle = testWhileIdle;
+
+        _pool = new CursorableLinkedList<ObjectTimestampPair<T>>();
+        startEvictor(_timeBetweenEvictionRunsMillis);
+    }
+
+    //--- public methods ---------------------------------------------
+
+    //--- configuration methods --------------------------------------
+
+    /**
+     * Returns the maximum number of objects that can be allocated by the pool
+     * (checked out to clients, or idle awaiting checkout) at a given time.
+     * When non-positive, there is no limit to the number of objects that can
+     * be managed by the pool at one time.
+     *
+     * @return the cap on the total number of object instances managed by the pool.
+     * @see #setMaxActive
+     */
+    public synchronized int getMaxActive() {
+        return _maxActive;
+    }
+
+    /**
+     * Sets the cap on the number of objects that can be allocated by the pool
+     * (checked out to clients, or idle awaiting checkout) at a given time. Use
+     * a negative value for no limit.
+     *
+     * @param maxActive The cap on the total number of object instances managed by the pool.
+     * Negative values mean that there is no limit to the number of objects allocated
+     * by the pool.
+     * @see #getMaxActive
+     */
+    public void setMaxActive(int maxActive) {
+        synchronized(this) {
+            _maxActive = maxActive;
+        }
+        allocate();
+    }
+
+    /**
+     * 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 synchronized 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 void setWhenExhaustedAction(byte whenExhaustedAction) {
+        synchronized(this) {
+            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.");
+            }
+        }
+        allocate();
+    }
+
+
+    /**
+     * 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 or equal to 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @return maximum number of milliseconds to block when borrowing an object.
+     * @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 or equal to 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @param maxWait maximum number of milliseconds to block when borrowing an object.
+     * @see #getMaxWait
+     * @see #setWhenExhaustedAction
+     * @see #WHEN_EXHAUSTED_BLOCK
+     */
+    public void setMaxWait(long maxWait) {
+        synchronized(this) {
+            _maxWait = maxWait;
+        }
+        allocate();
+    }
+
+    /**
+     * 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 synchronized int getMaxIdle() {
+        return _maxIdle;
+    }
+
+    /**
+     * Sets the cap on the number of "idle" instances in the pool.
+     * If maxIdle is set too low on heavily loaded systems it is possible you
+     * will see objects being destroyed and almost immediately new objects
+     * being created. This is a result of the active threads momentarily
+     * returning objects faster than they are requesting them them, causing the
+     * number of idle objects to rise above maxIdle. The best value for maxIdle
+     * for heavily loaded system will vary but the default is a good starting
+     * point.
+     * @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) {
+        synchronized(this) {
+            _maxIdle = maxIdle;
+        }
+        allocate();
+    }
+
+    /**
+     * Sets the minimum number of objects allowed in the pool
+     * before the evictor thread (if active) spawns new objects.
+     * Note that no objects are created when
+     * <code>numActive + numIdle >= maxActive.</code>
+     * This setting has no effect if the idle object evictor is disabled
+     * (i.e. if <code>timeBetweenEvictionRunsMillis <= 0</code>).
+     *
+     * @param minIdle The minimum number of objects.
+     * @see #getMinIdle
+     * @see #getTimeBetweenEvictionRunsMillis()
+     */
+    public void setMinIdle(int minIdle) {
+        synchronized(this) {
+            _minIdle = minIdle;
+        }
+        allocate();
+    }
+
+    /**
+     * Returns the minimum number of objects allowed in the pool
+     * before the evictor thread (if active) spawns new objects.
+     * (Note no objects are created when: numActive + numIdle >= maxActive)
+     *
+     * @return The minimum number of objects.
+     * @see #setMinIdle
+     */
+    public synchronized int getMinIdle() {
+        return _minIdle;
+    }
+
+    /**
+     * 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.
+     *
+     * @return <code>true</code> if objects are validated before being borrowed.
+     * @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.
+     *
+     * @param testOnBorrow <code>true</code> if objects should be validated before being borrowed.
+     * @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}.
+     *
+     * @return <code>true</code> when objects will be validated after returned to {@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}.
+     *
+     * @param testOnReturn <code>true</code> so objects will be validated after returned to {@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.
+     *
+     * @return number of milliseconds to sleep between evictor runs.
+     * @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.
+     *
+     * @param timeBetweenEvictionRunsMillis number of milliseconds to sleep between evictor runs.
+     * @see #getTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        startEvictor(_timeBetweenEvictionRunsMillis);
+    }
+
+    /**
+     * Returns the max number of objects to examine during each run of the
+     * idle object evictor thread (if any).
+     *
+     * @return max number of objects to examine during each evictor run.
+     * @see #setNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized int getNumTestsPerEvictionRun() {
+        return _numTestsPerEvictionRun;
+    }
+
+    /**
+     * Sets the max 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 #getNumIdle})/abs({@link #getNumTestsPerEvictionRun})</tt>
+     * tests will be run.  That is, when the value is <i>-n</i>, roughly one <i>n</i>th of the
+     * idle objects will be tested per run. When the value is positive, the number of tests
+     * actually performed in each run will be the minimum of this value and the number of instances
+     * idle in the pool.
+     *
+     * @param numTestsPerEvictionRun max number of objects to examine during each evictor run.
+     * @see #getNumTestsPerEvictionRun
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+    }
+
+    /**
+     * Returns the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor
+     * (if any).
+     *
+     * @return minimum amount of time an object may sit idle in the pool before it is eligible for eviction.
+     * @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 eligible 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.
+     * @param minEvictableIdleTimeMillis minimum amount of time an object may sit idle in the pool before
+     * it is eligible for eviction.
+     * @see #getMinEvictableIdleTimeMillis
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * Returns the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor
+     * (if any), with the extra condition that at least
+     * "minIdle" amount of object remain in the pool.
+     *
+     * @return minimum amount of time an object may sit idle in the pool before it is eligible for eviction.
+     * @since Pool 1.3
+     * @see #setSoftMinEvictableIdleTimeMillis
+     */
+    public synchronized long getSoftMinEvictableIdleTimeMillis() {
+        return _softMinEvictableIdleTimeMillis;
+    }
+
+    /**
+     * Sets the minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor
+     * (if any), with the extra condition that at least
+     * "minIdle" object instances remain in the pool.
+     * When non-positive, no objects will be evicted from the pool
+     * due to idle time alone.
+     *
+     * @param softMinEvictableIdleTimeMillis minimum amount of time an object may sit idle in the pool before
+     * it is eligible for eviction.
+     * @since Pool 1.3
+     * @see #getSoftMinEvictableIdleTimeMillis
+     */
+    public synchronized void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) {
+        _softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
+    }
+
+    /**
+     * 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.
+     *
+     * @return <code>true</code> when objects will be validated by the evictor.
+     * @see #setTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized 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.
+     *
+     * @param testWhileIdle <code>true</code> so objects will be validated by the evictor.
+     * @see #getTestWhileIdle
+     * @see #setTimeBetweenEvictionRunsMillis
+     */
+    public synchronized void setTestWhileIdle(boolean testWhileIdle) {
+        _testWhileIdle = testWhileIdle;
+    }
+
+    /**
+     * Whether or not the idle object pool acts as a LIFO queue. True means
+     * that borrowObject returns the most recently used ("last in") idle object
+     * in the pool (if there are idle instances available).  False means that
+     * the pool behaves as a FIFO queue - objects are taken from the idle object
+     * pool in the order that they are returned to the pool.
+     *
+     * @return <code>true</true> if the pool is configured to act as a LIFO queue
+     * @since 1.4
+     */
+     public synchronized boolean getLifo() {
+         return _lifo;
+     }
+
+     /**
+      * Sets the LIFO property of the pool. True means that borrowObject returns
+      * the most recently used ("last in") idle object in the pool (if there are
+      * idle instances available).  False means that the pool behaves as a FIFO
+      * queue - objects are taken from the idle object pool in the order that
+      * they are returned to the pool.
+      *
+      * @param lifo the new value for the LIFO property
+      * @since 1.4
+      */
+     public synchronized void setLifo(boolean lifo) {
+         this._lifo = lifo;
+     }
+
+    /**
+     * Sets my configuration.
+     *
+     * @param conf configuration to use.
+     * @see GenericObjectPool.Config
+     */
+    public void setConfig(GenericObjectPool.Config conf) {
+        synchronized (this) {
+            setMaxIdle(conf.maxIdle);
+            setMinIdle(conf.minIdle);
+            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);
+            setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis);
+            setLifo(conf.lifo);
+        }
+        allocate();
+    }
+
+    //-- ObjectPool methods ------------------------------------------
+
+    /**
+     * <p>Borrows an object from the pool.</p>
+     * 
+     * <p>If there is an idle instance available in the pool, then either the most-recently returned
+     * (if {@link #getLifo() lifo} == true) or "oldest" (lifo == false) instance sitting idle in the pool
+     * will be activated and returned.  If activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set
+     * to true and validation fails, the instance is destroyed and the next available instance is examined.
+     * This continues until either a valid instance is returned or there are no more idle instances available.</p>
+     * 
+     * <p>If there are no idle instances available in the pool, behavior depends on the {@link #getMaxActive() maxActive}
+     * and (if applicable) {@link #getWhenExhaustedAction() whenExhaustedAction} and {@link #getMaxWait() maxWait}
+     * properties. If the number of instances checked out from the pool is less than <code>maxActive,</code> a new
+     * instance is created, activated and (if applicable) validated and returned to the caller.</p>
+     * 
+     * <p>If the pool is exhausted (no available idle instances and no capacity to create new ones),
+     * this method will either block ({@link #WHEN_EXHAUSTED_BLOCK}), throw a <code>NoSuchElementException</code>
+     * ({@link #WHEN_EXHAUSTED_FAIL}), or grow ({@link #WHEN_EXHAUSTED_GROW} - ignoring maxActive).
+     * The length of time that this method will block when <code>whenExhaustedAction == WHEN_EXHAUSTED_BLOCK</code>
+     * is determined by the {@link #getMaxWait() maxWait} property.</p>
+     * 
+     * <p>When the pool is exhausted, multiple calling threads may be simultaneously blocked waiting for instances
+     * to become available.  As of pool 1.5, a "fairness" algorithm has been implemented to ensure that threads receive
+     * available instances in request arrival order.</p>
+     * 
+     * @return object instance
+     * @throws NoSuchElementException if an instance cannot be returned
+     */
+    @Override
+    public T borrowObject() throws Exception {
+        long starttime = System.currentTimeMillis();
+        Latch<T> latch = new Latch<T>();
+        byte whenExhaustedAction;
+        long maxWait;
+        synchronized (this) {
+            // Get local copy of current config. Can't sync when used later as
+            // it can result in a deadlock. Has the added advantage that config
+            // is consistent for entire method execution
+            whenExhaustedAction = _whenExhaustedAction;
+            maxWait = _maxWait;
+
+            // Add this request to the queue
+            _allocationQueue.add(latch);
+        }
+        // Work the allocation queue, allocating idle instances and
+        // instance creation permits in request arrival order
+        allocate();
+
+        for(;;) {
+            synchronized (this) {
+                assertOpen();
+            }
+
+            // If no object was allocated from the pool above
+            if(latch.getPair() == null) {
+                // check if we were allowed to create one
+                if(latch.mayCreate()) {
+                    // allow new object to be created
+                } else {
+                    // the pool is exhausted
+                    switch(whenExhaustedAction) {
+                        case WHEN_EXHAUSTED_GROW:
+                            // allow new object to be created
+                            synchronized (this) {
+                                // Make sure another thread didn't allocate us an object
+                                // or permit a new object to be created
+                                if (latch.getPair() == null && !latch.mayCreate()) {
+                                    _allocationQueue.remove(latch);
+                                    _numInternalProcessing++;
+                                }
+                            }
+                            break;
+                        case WHEN_EXHAUSTED_FAIL:
+                            synchronized (this) {
+                                // Make sure allocate hasn't already assigned an object
+                                // in a different thread or permitted a new object to be created
+                                if (latch.getPair() != null || latch.mayCreate()) {
+                                    break;
+                                }
+                                _allocationQueue.remove(latch);
+                            }
+                            throw new NoSuchElementException("Pool exhausted");
+                        case WHEN_EXHAUSTED_BLOCK:
+                            try {
+                                synchronized (latch) {
+                                    // Before we wait, make sure another thread didn't allocate us an object
+                                    // or permit a new object to be created
+                                    if (latch.getPair() == null && !latch.mayCreate()) {
+                                        if(maxWait <= 0) {
+                                            latch.wait();
+                                        } else {
+                                            // this code may be executed again after a notify then continue cycle
+                                            // so, need to calculate the amount of time to wait
+                                            final long elapsed = (System.currentTimeMillis() - starttime);
+                                            final long waitTime = maxWait - elapsed;
+                                            if (waitTime > 0)
+                                            {
+                                                latch.wait(waitTime);
+                                            }
+                                        }
+                                    } else {
+                                        break;
+                                    }
+                                }
+                                // see if we were awakened by a closing pool
+                                if(isClosed() == true) {
+                                    throw new IllegalStateException("Pool closed");
+                                }
+                            } catch(InterruptedException e) {
+                                boolean doAllocate = false;
+                                synchronized(this) {
+                                    // Need to handle the all three possibilities
+                                    if (latch.getPair() == null && !latch.mayCreate()) {
+                                        // Case 1: latch still in allocation queue
+                                        // Remove latch from the allocation queue
+                                        _allocationQueue.remove(latch);
+                                    } else if (latch.getPair() == null && latch.mayCreate()) {
+                                        // Case 2: latch has been given permission to create
+                                        //         a new object
+                                        _numInternalProcessing--;
+                                        doAllocate = true;
+                                    } else {
+                                        // Case 3: An object has been allocated
+                                        _numInternalProcessing--;
+                                        _numActive++;
+                                        returnObject(latch.getPair().getValue());
+                                    }
+                                }
+                                if (doAllocate) {
+                                    allocate();
+                                }
+                                Thread.currentThread().interrupt();
+                                throw e;
+                            }
+                            if(maxWait > 0 && ((System.currentTimeMillis() - starttime) >= maxWait)) {
+                                synchronized(this) {
+                                    // Make sure allocate hasn't already assigned an object
+                                    // in a different thread or permitted a new object to be created
+                                    if (latch.getPair() == null && !latch.mayCreate()) {
+                                        // Remove latch from the allocation queue
+                                        _allocationQueue.remove(latch);
+                                    } else {
+                                        break;
+                                    }
+                                }
+                                throw new NoSuchElementException("Timeout waiting for idle object");
+                            } else {
+                                continue; // keep looping
+                            }
+                        default:
+                            throw new IllegalArgumentException("WhenExhaustedAction property " + whenExhaustedAction +
+                                    " not recognized.");
+                    }
+                }
+            }
+
+            boolean newlyCreated = false;
+            if(null == latch.getPair()) {
+                try {
+                    T obj = _factory.makeObject();
+                    latch.setPair(new ObjectTimestampPair<T>(obj));
+                    newlyCreated = true;
+                } finally {
+                    if (!newlyCreated) {
+                        // object cannot be created
+                        synchronized (this) {
+                            _numInternalProcessing--;
+                            // No need to reset latch - about to throw exception
+                        }
+                        allocate();
+                    }
+                }
+            }
+            // activate & validate the object
+            try {
+                _factory.activateObject(latch.getPair().value);
+                if(_testOnBorrow &&
+                        !_factory.validateObject(latch.getPair().value)) {
+                    throw new Exception("ValidateObject failed");
+                }
+                synchronized(this) {
+                    _numInternalProcessing--;
+                    _numActive++;
+                }
+                return latch.getPair().value;
+            }
+            catch (Throwable e) {
+                PoolUtils.checkRethrow(e);
+                // object cannot be activated or is invalid
+                try {
+                    _factory.destroyObject(latch.getPair().value);
+                } catch (Throwable e2) {
+                    PoolUtils.checkRethrow(e2);
+                    // cannot destroy broken object
+                }
+                synchronized (this) {
+                    _numInternalProcessing--;
+                    if (!newlyCreated) {
+                        latch.reset();
+                        _allocationQueue.add(0, latch);
+                    }
+                }
+                allocate();
+                if(newlyCreated) {
+                    throw new NoSuchElementException("Could not create a validated object, cause: " + e.getMessage());
+                }
+                else {
+                    continue; // keep looping
+                }
+            }
+        }
+    }
+
+    /**
+     * Allocate available instances to latches in the allocation queue.  Then
+     * set _mayCreate to true for as many additional latches remaining in queue
+     * as _maxActive allows. While it is safe for GOP, for consistency with GKOP
+     * this method should not be called from inside a sync block. 
+     */
+    private synchronized void allocate() {
+        if (isClosed()) return;
+
+        // First use any objects in the pool to clear the queue
+        for (;;) {
+            if (!_pool.isEmpty() && !_allocationQueue.isEmpty()) {
+                Latch<T> latch = _allocationQueue.removeFirst();
+                latch.setPair( _pool.removeFirst());
+                _numInternalProcessing++;
+                synchronized (latch) {
+                    latch.notify();
+                }
+            } else {
+                break;
+            }
+        }
+
+        // Second utilise any spare capacity to create new objects
+        for(;;) {
+            if((!_allocationQueue.isEmpty()) && (_maxActive < 0 || (_numActive + _numInternalProcessing) < _maxActive)) {
+                Latch<T> latch = _allocationQueue.removeFirst();
+                latch.setMayCreate(true);
+                _numInternalProcessing++;
+                synchronized (latch) {
+                    latch.notify();
+                }
+            } else {
+                break;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>Activation of this method decrements the active count and attempts to destroy the instance.</p>
+     * 
+     * @throws Exception if the configured {@link PoolableObjectFactory} throws an exception destroying obj
+     */
+    @Override
+    public void invalidateObject(T obj) throws Exception {
+        try {
+            if (_factory != null) {
+                _factory.destroyObject(obj);
+            }
+        } finally {
+            synchronized (this) {
+                _numActive--;
+            }
+            allocate();
+        }
+    }
+
+    /**
+     * Clears any objects sitting idle in the pool by removing them from the
+     * idle instance pool and then invoking the configured 
+     * {@link PoolableObjectFactory#destroyObject(Object)} method on each idle
+     * instance. 
+     * 
+     * <p> Implementation notes:
+     * <ul><li>This method does not destroy or effect in any way instances that are
+     * checked out of the pool when it is invoked.</li>
+     * <li>Invoking this method does not prevent objects being
+     * returned to the idle instance pool, even during its execution. It locks
+     * the pool only during instance removal. Additional instances may be returned
+     * while removed items are being destroyed.</li>
+     * <li>Exceptions encountered destroying idle instances are swallowed.</li></ul></p>
+     */
+    @Override
+    public void clear() {
+        List<ObjectTimestampPair<T>> toDestroy = new ArrayList<ObjectTimestampPair<T>>();
+
+        synchronized(this) {
+            toDestroy.addAll(_pool);
+            _numInternalProcessing = _numInternalProcessing + _pool._size;
+            _pool.clear();
+        }
+        destroy(toDestroy, _factory);
+    }
+
+    /**
+     * Private method to destroy all the objects in a collection using the 
+     * supplied object factory.  Assumes that objects in the collection are
+     * instances of ObjectTimestampPair and that the object instances that
+     * they wrap were created by the factory.
+     * 
+     * @param c Collection of objects to destroy
+     * @param factory PoolableConnectionFactory used to destroy the objects
+     */
+    private void destroy(Collection<ObjectTimestampPair<T>> c, PoolableObjectFactory<T> factory) {
+        for (Iterator<ObjectTimestampPair<T>> it = c.iterator(); it.hasNext();) {
+            try {
+                factory.destroyObject(it.next().value);
+            } catch(Exception e) {
+                // ignore error, keep destroying the rest
+            } finally {
+                synchronized(this) {
+                    _numInternalProcessing--;
+                }
+                allocate();
+            }
+        }
+    }
+
+    /**
+     * Return the number of instances currently borrowed from this pool.
+     *
+     * @return the number of instances currently borrowed from this pool
+     */
+    @Override
+    public synchronized int getNumActive() {
+        return _numActive;
+    }
+
+    /**
+     * Return the number of instances currently idle in this pool.
+     *
+     * @return the number of instances currently idle in this pool
+     */
+    @Override
+    public synchronized int getNumIdle() {
+        return _pool.size();
+    }
+
+    /**
+     * <p>Returns an object instance to the pool.</p>
+     * 
+     * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances
+     * has reached this value, the returning instance is destroyed.</p>
+     * 
+     * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned
+     * to the idle instance pool.  In this case, if validation fails, the instance is destroyed.</p>
+     * 
+     * <p><strong>Note: </strong> There is no guard to prevent an object
+     * being returned to the pool multiple times. Clients are expected to
+     * discard references to returned objects and ensure that an object is not
+     * returned to the pool multiple times in sequence (i.e., without being
+     * borrowed again between returns). Violating this contract will result in
+     * the same object appearing multiple times in the pool and pool counters
+     * (numActive, numIdle) returning incorrect values.</p>
+     * 
+     * @param obj instance to return to the pool
+     */
+    @Override
+    public void returnObject(T obj) throws Exception {
+        try {
+            addObjectToPool(obj, true);
+        } catch (Exception e) {
+            if (_factory != null) {
+                try {
+                    _factory.destroyObject(obj);
+                } catch (Exception e2) {
+                    // swallowed
+                }
+                // TODO: Correctness here depends on control in addObjectToPool.
+                // These two methods should be refactored, removing the
+                // "behavior flag", decrementNumActive, from addObjectToPool.
+                synchronized(this) {
+                    _numActive--;
+                }
+                allocate();
+            }
+        }
+    }
+
+    /**
+     * <p>Adds an object to the pool.</p>
+     * 
+     * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool.
+     * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance
+     * is destroyed.</p>
+     * 
+     * <p>Calls {@link #allocate()} on successful completion</p>
+     * 
+     * @param obj instance to add to the pool
+     * @param decrementNumActive whether or not to decrement the active count
+     * @throws Exception
+     */
+    private void addObjectToPool(T obj, boolean decrementNumActive) throws Exception {
+        boolean success = true;
+        if(_testOnReturn && !(_factory.validateObject(obj))) {
+            success = false;
+        } else {
+            _factory.passivateObject(obj);
+        }
+
+        boolean shouldDestroy = !success;
+
+        // Add instance to pool if there is room and it has passed validation
+        // (if testOnreturn is set)
+        boolean doAllocate = false;
+        synchronized (this) {
+            if (isClosed()) {
+                shouldDestroy = true;
+            } else {
+                if((_maxIdle >= 0) && (_pool.size() >= _maxIdle)) {
+                    shouldDestroy = true;
+                } else if(success) {
+                    // borrowObject always takes the first element from the queue,
+                    // so for LIFO, push on top, FIFO add to end
+                    if (_lifo) {
+                        _pool.addFirst(new ObjectTimestampPair<T>(obj));
+                    } else {
+                        _pool.addLast(new ObjectTimestampPair<T>(obj));
+                    }
+                    if (decrementNumActive) {
+                        _numActive--;
+                    }
+                    doAllocate = true;
+                }
+            }
+        }
+        if (doAllocate) {
+            allocate();
+        }
+
+        // Destroy the instance if necessary
+        if(shouldDestroy) {
+            try {
+                _factory.destroyObject(obj);
+            } catch(Exception e) {
+                // ignored
+            }
+            // Decrement active count *after* destroy if applicable
+            if (decrementNumActive) {
+                synchronized(this) {
+                    _numActive--;
+                }
+                allocate();
+            }
+        }
+
+    }
+
+    /**
+     * <p>Closes the pool.  Once the pool is closed, {@link #borrowObject()}
+     * will fail with IllegalStateException, but {@link #returnObject(Object)} and
+     * {@link #invalidateObject(Object)} will continue to work, with returned objects
+     * destroyed on return.</p>
+     * 
+     * <p>Destroys idle instances in the pool by invoking {@link #clear()}.</p> 
+     * 
+     * @throws Exception
+     */
+    @Override
+    public void close() throws Exception {
+        super.close();
+        synchronized (this) {
+            clear();
+            startEvictor(-1L);
+
+            while(_allocationQueue.size() > 0) {
+                Latch<T> l = _allocationQueue.removeFirst();
+                
+                synchronized (l) {
+                    // notify the waiting thread
+                    l.notify();
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the {@link PoolableObjectFactory factory} this pool uses
+     * to create new instances. Trying to change
+     * the <code>factory</code> while there are borrowed objects will
+     * throw an {@link IllegalStateException}.  If there are instances idle
+     * in the pool when this method is invoked, these will be destroyed
+     * using the original factory.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to create new instances.
+     * @throws IllegalStateException when the factory cannot be set at this time
+     * @deprecated to be removed in version 2.0
+     */
+    @Deprecated
+    @Override
+    public void setFactory(PoolableObjectFactory<T> factory) throws IllegalStateException {
+        List<ObjectTimestampPair<T>> toDestroy = new ArrayList<ObjectTimestampPair<T>>();
+        final PoolableObjectFactory<T> oldFactory = _factory;
+        synchronized (this) {
+            assertOpen();
+            if(0 < getNumActive()) {
+                throw new IllegalStateException("Objects are already active");
+            } else {
+                toDestroy.addAll(_pool);
+                _numInternalProcessing = _numInternalProcessing + _pool._size;
+                _pool.clear();
+            }
+            _factory = factory;
+        }
+        destroy(toDestroy, oldFactory); 
+    }
+
+    /**
+     * <p>Perform <code>numTests</code> idle object eviction tests, evicting
+     * examined objects that meet the criteria for eviction. If
+     * <code>testWhileIdle</code> is true, examined objects are validated
+     * when visited (and removed if invalid); otherwise only objects that
+     * have been idle for more than <code>minEvicableIdletimeMillis</code>
+     * are removed.</p>
+     *
+     * <p>Successive activations of this method examine objects in
+     * in sequence, cycling through objects in oldest-to-youngest order.</p>
+     *
+     * @throws Exception if the pool is closed or eviction fails.
+     */
+    public void evict() throws Exception {
+        assertOpen();
+        synchronized (this) {
+            if(_pool.isEmpty()) {
+                return;
+            }
+            if (null == _evictionCursor) {
+                _evictionCursor = _pool.cursor(_lifo ? _pool.size() : 0);
+            }
+        }
+
+        for (int i=0,m=getNumTests();i<m;i++) {
+            final ObjectTimestampPair<T> pair;
+            synchronized (this) {
+                if ((_lifo && !_evictionCursor.hasPrevious()) ||
+                        !_lifo && !_evictionCursor.hasNext()) {
+                    _evictionCursor.close();
+                    _evictionCursor = _pool.cursor(_lifo ? _pool.size() : 0);
+                }
+
+                pair = _lifo ?
+                         _evictionCursor.previous() :
+                         _evictionCursor.next();
+
+                _evictionCursor.remove();
+                _numInternalProcessing++;
+            }
+
+            boolean removeObject = false;
+            final long idleTimeMilis = System.currentTimeMillis() - pair.tstamp;
+            if ((getMinEvictableIdleTimeMillis() > 0) &&
+                    (idleTimeMilis > getMinEvictableIdleTimeMillis())) {
+                removeObject = true;
+            } else if ((getSoftMinEvictableIdleTimeMillis() > 0) &&
+                    (idleTimeMilis > getSoftMinEvictableIdleTimeMillis()) &&
+                    ((getNumIdle() + 1)> getMinIdle())) { // +1 accounts for object we are processing
+                removeObject = true;
+            }
+            if(getTestWhileIdle() && !removeObject) {
+                boolean active = false;
+                try {
+                    _factory.activateObject(pair.value);
+                    active = true;
+                } catch(Exception e) {
+                    removeObject=true;
+                }
+                if(active) {
+                    if(!_factory.validateObject(pair.value)) {
+                        removeObject=true;
+                    } else {
+                        try {
+                            _factory.passivateObject(pair.value);
+                        } catch(Exception e) {
+                            removeObject=true;
+                        }
+                    }
+                }
+            }
+
+            if (removeObject) {
+                try {
+                    _factory.destroyObject(pair.value);
+                } catch(Exception e) {
+                    // ignored
+                }
+            }
+            synchronized (this) {
+                if(!removeObject) {
+                    _evictionCursor.add(pair);
+                    if (_lifo) {
+                        // Skip over the element we just added back
+                        _evictionCursor.previous();
+                    }
+                }
+                _numInternalProcessing--;
+            }
+        }
+        allocate();
+    }
+
+    /**
+     * Check to see if we are below our minimum number of objects
+     * if so enough to bring us back to our minimum.
+     *
+     * @throws Exception when {@link #addObject()} fails.
+     */
+    private void ensureMinIdle() throws Exception {
+        // this method isn't synchronized so the
+        // calculateDeficit is done at the beginning
+        // as a loop limit and a second time inside the loop
+        // to stop when another thread already returned the
+        // needed objects
+        int objectDeficit = calculateDeficit(false);
+        for ( int j = 0 ; j < objectDeficit && calculateDeficit(true) > 0 ; j++ ) {
+            try {
+                addObject();
+            } finally {
+                synchronized (this) {
+                    _numInternalProcessing--;
+                }
+                allocate();
+            }
+        }
+    }
+
+    /**
+     * This returns the number of objects to create during the pool
+     * sustain cycle. This will ensure that the minimum number of idle
+     * instances is maintained without going past the maxActive value.
+     *
+     * @param incrementInternal - Should the count of objects currently under
+     *                            some form of internal processing be
+     *                            incremented?
+     * @return The number of objects to be created
+     */
+    private synchronized int calculateDeficit(boolean incrementInternal) {
+        int objectDeficit = getMinIdle() - getNumIdle();
+        if (_maxActive > 0) {
+            int growLimit = Math.max(0,
+                    getMaxActive() - getNumActive() - getNumIdle() - _numInternalProcessing);
+            objectDeficit = Math.min(objectDeficit, growLimit);
+        }
+        if (incrementInternal && objectDeficit >0) {
+            _numInternalProcessing++;
+        }
+        return objectDeficit;
+    }
+
+    /**
+     * Create an object, and place it into the pool.
+     * addObject() is useful for "pre-loading" a pool with idle objects.
+     */
+    @Override
+    public void addObject() throws Exception {
+        assertOpen();
+        if (_factory == null) {
+            throw new IllegalStateException("Cannot add objects without a factory.");
+        }
+        T obj = _factory.makeObject();
+        try {
+            assertOpen();
+            addObjectToPool(obj, false);
+        } catch (IllegalStateException ex) { // Pool closed
+            try {
+                _factory.destroyObject(obj);
+            } catch (Exception ex2) {
+                // swallow
+            }
+            throw ex;
+        }
+    }
+
+    //--- non-public methods ----------------------------------------
+
+    /**
+     * Start the eviction thread or service, or when
+     * <i>delay</i> is non-positive, stop it
+     * if it is already running.
+     *
+     * @param delay milliseconds between evictor runs.
+     */
+    protected synchronized void startEvictor(long delay) {
+        if(null != _evictor) {
+            EvictionTimer.cancel(_evictor);
+            _evictor = null;
+        }
+        if(delay > 0) {
+            _evictor = new Evictor();
+            EvictionTimer.schedule(_evictor, delay, delay);
+        }
+    }
+
+    /**
+     * Returns pool info including {@link #getNumActive()}, {@link #getNumIdle()}
+     * and a list of objects idle in the pool with their idle times.
+     * 
+     * @return string containing debug information
+     */
+    synchronized String debugInfo() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Active: ").append(getNumActive()).append("\n");
+        buf.append("Idle: ").append(getNumIdle()).append("\n");
+        buf.append("Idle Objects:\n");
+        Iterator<ObjectTimestampPair<T>> it = _pool.iterator();
+        long time = System.currentTimeMillis();
+        while(it.hasNext()) {
+            ObjectTimestampPair<T> pair = it.next();
+            buf.append("\t").append(pair.value).append("\t").append(time - pair.tstamp).append("\n");
+        }
+        return buf.toString();
+    }
+
+    /** 
+     * Returns the number of tests to be performed in an Evictor run,
+     * based on the current value of <code>numTestsPerEvictionRun</code>
+     * and the number of idle instances in the pool.
+     * 
+     * @see #setNumTestsPerEvictionRun
+     * @return the number of tests for the Evictor to run
+     */
+    private int getNumTests() {
+        if(_numTestsPerEvictionRun >= 0) {
+            return Math.min(_numTestsPerEvictionRun, _pool.size());
+        } else {
+            return(int)(Math.ceil(_pool.size()/Math.abs((double)_numTestsPerEvictionRun)));
+        }
+    }
+
+    //--- inner classes ----------------------------------------------
+
+    /**
+     * The idle object evictor {@link TimerTask}.
+     * @see GenericObjectPool#setTimeBetweenEvictionRunsMillis
+     */
+    private class Evictor extends TimerTask {
+        /**
+         * Run pool maintenance.  Evict objects qualifying for eviction and then
+         * invoke {@link GenericObjectPool#ensureMinIdle()}.
+         */
+        @Override
+        public void run() {
+            try {
+                evict();
+            } catch(Exception e) {
+                // ignored
+            } catch(OutOfMemoryError oome) {
+                // Log problem but give evictor thread a chance to continue in
+                // case error is recoverable
+                oome.printStackTrace(System.err);
+            }
+            try {
+                ensureMinIdle();
+            } catch(Exception e) {
+                // ignored
+            }
+        }
+    }
+
+    /**
+     * 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 {
+        //CHECKSTYLE: stop VisibilityModifier
+        /**
+         * @see GenericObjectPool#setMaxIdle
+         */
+        public int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
+        /**
+         * @see GenericObjectPool#setMinIdle
+         */
+        public int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;
+        /**
+         * @see GenericObjectPool#setMaxActive
+         */
+        public int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
+        /**
+         * @see GenericObjectPool#setMaxWait
+         */
+        public long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
+        /**
+         * @see GenericObjectPool#setWhenExhaustedAction
+         */
+        public byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+        /**
+         * @see GenericObjectPool#setTestOnBorrow
+         */
+        public boolean testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
+        /**
+         * @see GenericObjectPool#setTestOnReturn
+         */
+        public boolean testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
+        /**
+         * @see GenericObjectPool#setTestWhileIdle
+         */
+        public boolean testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
+        /**
+         * @see GenericObjectPool#setTimeBetweenEvictionRunsMillis
+         */
+        public long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+        /**
+         * @see GenericObjectPool#setNumTestsPerEvictionRun
+         */
+        public int numTestsPerEvictionRun =  GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+        /**
+         * @see GenericObjectPool#setMinEvictableIdleTimeMillis
+         */
+        public long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+        /**
+         * @see GenericObjectPool#setSoftMinEvictableIdleTimeMillis
+         */
+        public long softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+        /**
+         * @see GenericObjectPool#setLifo
+         */
+        public boolean lifo = GenericObjectPool.DEFAULT_LIFO;
+        //CHECKSTYLE: resume VisibilityModifier
+    }
+
+    /**
+     * Latch used to control allocation order of objects to threads to ensure
+     * fairness. That is, objects are allocated to threads in the order that
+     * threads request objects.
+     */
+    private static final class Latch<T> {
+        
+        /** object timestamp pair allocated to this latch */
+        private ObjectTimestampPair<T> _pair;
+        
+        /** Whether or not this latch may create an object instance */
+        private boolean _mayCreate = false;
+
+        /**
+         * Returns ObjectTimestampPair allocated to this latch
+         * @return ObjectTimestampPair allocated to this latch
+         */
+        private synchronized ObjectTimestampPair<T> getPair() {
+            return _pair;
+        }
+        
+        /**
+         * Sets ObjectTimestampPair on this latch
+         * @param pair ObjectTimestampPair allocated to this latch
+         */
+        private synchronized void setPair(ObjectTimestampPair<T> pair) {
+            _pair = pair;
+        }
+
+        /**
+         * Whether or not this latch may create an object instance 
+         * @return true if this latch has an instance creation permit
+         */
+        private synchronized boolean mayCreate() {
+            return _mayCreate;
+        }
+        
+        /**
+         * Sets the mayCreate property
+         * @param mayCreate new value for mayCreate
+         */
+        private synchronized void setMayCreate(boolean mayCreate) {
+            _mayCreate = mayCreate;
+        }
+
+        /**
+         * Reset the latch data. Used when an allocation fails and the latch
+         * needs to be re-added to the queue.
+         */
+        private synchronized void reset() {
+            _pair = null;
+            _mayCreate = false;
+        }
+    }
+
+
+    //--- private attributes ---------------------------------------
+
+    /**
+     * The cap on the number of idle instances in the pool.
+     * @see #setMaxIdle
+     * @see #getMaxIdle
+     */
+    private int _maxIdle = DEFAULT_MAX_IDLE;
+
+    /**
+    * The cap on the minimum number of idle instances in the pool.
+    * @see #setMinIdle
+    * @see #getMinIdle
+    */
+    private int _minIdle = DEFAULT_MIN_IDLE;
+
+    /**
+     * The cap on the total number of active instances from the pool.
+     * @see #setMaxActive
+     * @see #getMaxActive
+     */
+    private 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 or equal to 0, the {@link #borrowObject} method
+     * may block indefinitely.
+     *
+     * @see #setMaxWait
+     * @see #getMaxWait
+     * @see #WHEN_EXHAUSTED_BLOCK
+     * @see #setWhenExhaustedAction
+     * @see #getWhenExhaustedAction
+     */
+    private 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
+     */
+    private 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
+     */
+    private volatile 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
+     */
+    private volatile 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
+     */
+    private 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
+     */
+    private long _timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+
+    /**
+     * The max 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 #getNumIdle})/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
+     */
+    private int _numTestsPerEvictionRun =  DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+
+    /**
+     * The minimum amount of time an object may sit idle in the pool
+     * before it is eligible 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
+     */
+    private long _minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+    /**
+     * The minimum amount of time an object may sit idle in the pool
+     * before it is eligible for eviction by the idle object evictor
+     * (if any), with the extra condition that at least
+     * "minIdle" amount of object remain in the pool.
+     * When non-positive, no objects will be evicted from the pool
+     * due to idle time alone.
+     *
+     * @see #setSoftMinEvictableIdleTimeMillis
+     * @see #getSoftMinEvictableIdleTimeMillis
+     */
+    private long _softMinEvictableIdleTimeMillis = DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+    /** Whether or not the pool behaves as a LIFO queue (last in first out) */
+    private boolean _lifo = DEFAULT_LIFO;
+
+    /** My pool. */
+    private CursorableLinkedList<ObjectTimestampPair<T>> _pool = null;
+
+    /** Eviction cursor - keeps track of idle object evictor position */
+    private CursorableLinkedList<ObjectTimestampPair<T>>.Cursor _evictionCursor = null;
+
+    /** My {@link PoolableObjectFactory}. */
+    private PoolableObjectFactory<T> _factory = null;
+
+    /**
+     * The number of objects {@link #borrowObject} borrowed
+     * from the pool, but not yet returned.
+     */
+    private int _numActive = 0;
+
+    /**
+     * My idle object eviction {@link TimerTask}, if any.
+     */
+    private Evictor _evictor = null;
+
+    /**
+     * The number of objects subject to some form of internal processing
+     * (usually creation or destruction) that should be included in the total
+     * number of objects but are neither active nor idle.
+     */
+    private int _numInternalProcessing = 0;
+
+    /**
+     * Used to track the order in which threads call {@link #borrowObject()} so
+     * that objects can be allocated in the order in which the threads requested
+     * them.
+     */
+    private final LinkedList<Latch<T>> _allocationQueue = new LinkedList<Latch<T>>();
+
+}
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..e706782
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/GenericObjectPoolFactory.java
@@ -0,0 +1,459 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.ObjectPoolFactory;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+/**
+ * A factory for creating {@link GenericObjectPool} instances.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @see GenericObjectPool
+ * @see ObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class GenericObjectPoolFactory<T> implements ObjectPoolFactory<T> {
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory) {
+        this(factory,GenericObjectPool.DEFAULT_MAX_ACTIVE,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_MIN_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_TES [...]
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param config a non-<code>null</code> GenericObjectPool.Config describing the configuration.
+     * @throws NullPointerException when config is <code>null</code>.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, GenericObjectPool.Config)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, GenericObjectPool.Config config) throws NullPointerException {
+        this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle,config.softMinEvictableIdleTimeMillis, config.lifo);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive) {
+        this(factory,maxActive,GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,GenericObjectPool.DEFAULT_MAX_WAIT,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_MIN_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);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_MIN_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);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param testOnBorrow whether to validate objects before they are returned by the borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to the returnObject.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, boolean, boolean)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,GenericObjectPool.DEFAULT_MAX_IDLE,GenericObjectPool.DEFAULT_MIN_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);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in my pool.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericObjectPool.DEFAULT_MIN_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);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in my pool.
+     * @param testOnBorrow whether to validate objects before they are returned by the borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to the returnObject.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, boolean, boolean)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericObjectPool.DEFAULT_MIN_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);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in my pool.
+     * @param testOnBorrow whether to validate objects before they are returned by the borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to the returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, boolean, boolean, long, int, long, boolean)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,GenericObjectPool.DEFAULT_MIN_IDLE,testOnBorrow,testOnReturn,timeBetweenEvictionRunsMillis,numTestsPerEvictionRun,minEvictableIdleTimeMillis,testWhileIdle, GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in my pool.
+     * @param minIdle the minimum number of idle objects in my pool.
+     * @param testOnBorrow whether to validate objects before they are returned by the borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to the returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread.
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, int, boolean, boolean, long, int, long, boolean)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,minIdle,testOnBorrow,testOnReturn,timeBetweenEvictionRunsMillis,numTestsPerEvictionRun,minEvictableIdleTimeMillis,testWhileIdle, GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in my pool.
+     * @param minIdle the minimum number of idle objects in my pool.
+     * @param testOnBorrow whether to validate objects before they are returned by the borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to the returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread.
+     * @param softMinEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction with the extra condition that at least "minIdle" amount of object remain in the pool.
+     * @since Pool 1.3
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, int, boolean, boolean, long, int, long, boolean, long)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis) {
+        this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,minIdle,testOnBorrow,testOnReturn,timeBetweenEvictionRunsMillis,numTestsPerEvictionRun,minEvictableIdleTimeMillis,testWhileIdle,softMinEvictableIdleTimeMillis, GenericObjectPool.DEFAULT_LIFO);
+    }
+
+    /**
+     * Create a new GenericObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxActive maximum number of objects that can be borrowed from created pools at one time.
+     * @param whenExhaustedAction the action to take when the pool is exhausted.
+     * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted.
+     * @param maxIdle the maximum number of idle objects in my pool.
+     * @param minIdle the minimum number of idle objects in my pool.
+     * @param testOnBorrow whether to validate objects before they are returned by the borrowObject.
+     * @param testOnReturn whether to validate objects after they are returned to the returnObject.
+     * @param timeBetweenEvictionRunsMillis the number of milliseconds to sleep between examining idle objects for eviction.
+     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread.
+     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction.
+     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread.
+     * @param softMinEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligible for eviction with the extra condition that at least "minIdle" amount of object remain in the pool.
+     * @param lifo whether or not objects are returned in last-in-first-out order from the idle object pool.
+     * @since Pool 1.4
+     * @see GenericObjectPool#GenericObjectPool(PoolableObjectFactory, int, byte, long, int, int, boolean, boolean, long, int, long, boolean, long, boolean)
+     */
+    public GenericObjectPoolFactory(PoolableObjectFactory<T> factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis, boolean lifo) {
+        _maxIdle = maxIdle;
+        _minIdle = minIdle;
+        _maxActive = maxActive;
+        _maxWait = maxWait;
+        _whenExhaustedAction = whenExhaustedAction;
+        _testOnBorrow = testOnBorrow;
+        _testOnReturn = testOnReturn;
+        _testWhileIdle = testWhileIdle;
+        _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        _numTestsPerEvictionRun = numTestsPerEvictionRun;
+        _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        _softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
+        _lifo = lifo;
+        _factory = factory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ObjectPool<T> createPool() {
+        return new GenericObjectPool<T>(_factory,_maxActive,_whenExhaustedAction,_maxWait,_maxIdle,_minIdle,_testOnBorrow,_testOnReturn,_timeBetweenEvictionRunsMillis,_numTestsPerEvictionRun,_minEvictableIdleTimeMillis,_testWhileIdle,_softMinEvictableIdleTimeMillis,_lifo);
+    }
+
+    
+    /**
+     * @return the {@link GenericObjectPool#getMaxIdle() maxIdle} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMaxIdle() {
+        return _maxIdle;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getMinIdle() minIdle} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMinIdle() {
+        return _minIdle;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getMaxActive() maxActive} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getMaxActive() {
+        return _maxActive;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getMaxWait() maxWait} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getMaxWait() {
+        return _maxWait;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getWhenExhaustedAction() whenExhaustedAction} setting for pools
+     * created by this factory.
+     * @since 1.5.5
+     */
+    public byte getWhenExhaustedAction() {
+        return _whenExhaustedAction;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getTestOnBorrow() testOnBorrow} setting for pools
+     * created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getTestOnBorrow() {
+        return _testOnBorrow;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getTestOnReturn() testOnReturn} setting for pools
+     * created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getTestOnReturn() {
+        return _testOnReturn;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getTestWhileIdle() testWhileIdle} setting for pools
+     * created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getTestWhileIdle() {
+        return _testWhileIdle;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getTimeBetweenEvictionRunsMillis() {
+        return _timeBetweenEvictionRunsMillis;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getNumTestsPerEvictionRun() numTestsPerEvictionRun}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public int getNumTestsPerEvictionRun() {
+        return _numTestsPerEvictionRun;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getMinEvictableIdleTimeMillis() minEvictableIdleTimeMillis}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getMinEvictableIdleTimeMillis() {
+        return _minEvictableIdleTimeMillis;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis() softMinEvicatableIdleTimeMillis}
+     * setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public long getSoftMinEvictableIdleTimeMillis() {
+        return _softMinEvictableIdleTimeMillis;
+    }
+
+    /**
+     * @return the {@link GenericObjectPool#getLifo() lifo} setting for pools created by this factory.
+     * @since 1.5.5
+     */
+    public boolean getLifo() {
+        return _lifo;
+    }
+
+    /**
+     * @return the {@link PoolableObjectFactory} used by pools created by this factory
+     */
+    public PoolableObjectFactory<T> getFactory() {
+        return _factory;
+    }
+  
+    /**
+     * The {@link GenericObjectPool#getMaxIdle() maxIdle} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getMaxIdle()}.
+     */
+    @Deprecated
+    protected int _maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
+    
+    /**
+     * The {@link GenericObjectPool#getMinIdle() minIdle} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getMinIdle()}.
+     */
+    @Deprecated
+    protected int _minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;
+    
+    /**
+     * The {@link GenericObjectPool#getMaxActive() maxActive} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getMaxActive()}.
+     */
+    @Deprecated
+    protected int _maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
+    
+    /**
+     * The {@link GenericObjectPool#getMaxWait() maxWait} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getMaxWait()}.
+     */
+    @Deprecated
+    protected long _maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
+    
+    /**
+     * The {@link GenericObjectPool#getWhenExhaustedAction() whenExhaustedAction} setting for pools
+     * created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getWhenExhaustedAction()}.
+     */
+    @Deprecated
+    protected byte _whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
+    
+    /**
+     * The {@link GenericObjectPool#getTestOnBorrow() testOnBorrow} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getTestOnBorrow()}.
+     */
+    @Deprecated
+    protected boolean _testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
+    
+    /**
+     * The {@link GenericObjectPool#getTestOnReturn() testOnReturn} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getTestOnReturn()}.
+     */
+    @Deprecated
+    protected boolean _testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
+    
+    /**
+     * The {@link GenericObjectPool#getTestWhileIdle() testWhileIdle} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getTestWhileIdle()}.
+     */
+    @Deprecated
+    protected boolean _testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
+    
+    /**
+     * The {@link GenericObjectPool#getTimeBetweenEvictionRunsMillis() timeBetweenEvictionRunsMillis}
+     * setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getTimeBetweenEvictionRunsMillis()}.
+     */
+    @Deprecated
+    protected long _timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+    
+    /**
+     * The {@link GenericObjectPool#getNumTestsPerEvictionRun() numTestsPerEvictionRun} setting
+     * for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getNumTestsPerEvictionRun()}.
+     */
+    @Deprecated
+    protected int _numTestsPerEvictionRun =  GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+    
+    /**
+     * The {@link GenericObjectPool#getMinEvictableIdleTimeMillis() minEvictableIdleTimeMillis}
+     * setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getMinEvictableIdleTimeMillis()}.
+     */
+    @Deprecated
+    protected long _minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    
+    /**
+     * The {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis() softMinEvictableIdleTimeMillis}
+     * setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getSoftMinEvictableIdleTimeMillis()}.
+     */
+    @Deprecated
+    protected long _softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+    
+    /**
+     * The {@link GenericObjectPool#getLifo() lifo} setting for pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getLifo()}.
+     */
+    @Deprecated
+    protected boolean _lifo = GenericObjectPool.DEFAULT_LIFO;
+    
+    /**
+     * The {@link PoolableObjectFactory} used by pools created by this factory.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getFactory()}.
+     */
+    @Deprecated
+    protected PoolableObjectFactory<T> _factory = null;
+
+}
diff --git a/src/java/org/apache/commons/pool/impl/SoftReferenceObjectPool.java b/src/java/org/apache/commons/pool/impl/SoftReferenceObjectPool.java
new file mode 100644
index 0000000..cf4c606
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/SoftReferenceObjectPool.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.pool.BaseObjectPool;
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolUtils;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+/**
+ * A {@link java.lang.ref.SoftReference SoftReference} based
+ * {@link ObjectPool}.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class SoftReferenceObjectPool<T> extends BaseObjectPool<T> implements ObjectPool<T> {
+    /**
+     * Create a <code>SoftReferenceObjectPool</code> without a factory.
+     * {@link #setFactory(PoolableObjectFactory) setFactory} should be called
+     * before any attempts to use the pool are made.
+     * Generally speaking you should prefer the {@link #SoftReferenceObjectPool(PoolableObjectFactory)} constructor.
+     *
+     * @see #SoftReferenceObjectPool(PoolableObjectFactory)
+     * @deprecated to be removed in pool 2.0.  Use {@link #SoftReferenceObjectPool(PoolableObjectFactory)}.
+     */
+    @Deprecated
+    public SoftReferenceObjectPool() {
+        _pool = new ArrayList<SoftReference<T>>();
+        _factory = null;
+    }
+
+    /**
+     * Create a <code>SoftReferenceObjectPool</code> with the specified factory.
+     *
+     * @param factory object factory to use.
+     */
+    public SoftReferenceObjectPool(PoolableObjectFactory<T> factory) {
+        _pool = new ArrayList<SoftReference<T>>();
+        _factory = factory;
+    }
+
+    /**
+     * Create a <code>SoftReferenceObjectPool</code> with the specified factory and initial idle object count.
+     *
+     * @param factory object factory to use.
+     * @param initSize initial size to attempt to prefill the pool.
+     * @throws Exception when there is a problem prefilling the pool.
+     * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
+     * @deprecated because this is a SoftReference pool, prefilled idle obejects may be garbage collected before they are used.
+     *      To be removed in Pool 2.0.
+     */
+    @Deprecated
+    public SoftReferenceObjectPool(PoolableObjectFactory<T> factory, int initSize) throws Exception, IllegalArgumentException {
+        if (factory == null) {
+            throw new IllegalArgumentException("factory required to prefill the pool.");
+        }
+        _pool = new ArrayList<SoftReference<T>>(initSize);
+        _factory = factory;
+        PoolUtils.prefill(this, initSize);
+    }
+
+    /**
+     * <p>Borrow an object from the pool.  If there are no idle instances available in the pool, the configured
+     * factory's {@link PoolableObjectFactory#makeObject()} method is invoked to create a new instance.</p>
+     * 
+     * <p>All instances are {@link PoolableObjectFactory#activateObject(Object) activated} and
+     * {@link PoolableObjectFactory#validateObject(Object) validated} before being returned by this
+     * method.  If validation fails or an exception occurs activating or validating an idle instance,
+     * the failing instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed} and another
+     * instance is retrieved from the pool, validated and activated.  This process continues until either the
+     * pool is empty or an instance passes validation.  If the pool is empty on activation or
+     * it does not contain any valid instances, the factory's <code>makeObject</code> method is used
+     * to create a new instance.  If the created instance either raises an exception on activation or
+     * fails validation, <code>NoSuchElementException</code> is thrown. Exceptions thrown by <code>MakeObject</code>
+     * are propagated to the caller; but other than <code>ThreadDeath</code> or <code>VirtualMachineError</code>,
+     * exceptions generated by activation, validation or destroy methods are swallowed silently.</p>
+     * 
+     * @throws NoSuchElementException if a valid object cannot be provided
+     * @throws IllegalStateException if invoked on a {@link #close() closed} pool
+     * @throws Exception if an exception occurs creating a new instance
+     * @return a valid, activated object instance
+     */
+    @Override
+    public synchronized T borrowObject() throws Exception {
+        assertOpen();
+        T obj = null;
+        boolean newlyCreated = false;
+        while(null == obj) {
+            if(_pool.isEmpty()) {
+                if(null == _factory) {
+                    throw new NoSuchElementException();
+                } else {
+                    newlyCreated = true;
+                    obj = _factory.makeObject();
+                }
+            } else {
+                SoftReference<T> ref = _pool.remove(_pool.size() - 1);
+                obj = ref.get();
+                ref.clear(); // prevent this ref from being enqueued with refQueue.
+            }
+            if (null != _factory && null != obj) {
+                try {
+                    _factory.activateObject(obj);
+                    if (!_factory.validateObject(obj)) {
+                        throw new Exception("ValidateObject failed");
+                    }
+                } catch (Throwable t) {
+                    PoolUtils.checkRethrow(t);
+                    try {
+                        _factory.destroyObject(obj);
+                    } catch (Throwable t2) {
+                        PoolUtils.checkRethrow(t2);
+                        // Swallowed
+                    } finally {
+                        obj = null;
+                    }
+                    if (newlyCreated) {
+                        throw new NoSuchElementException(
+                            "Could not create a validated object, cause: " +
+                            t.getMessage());
+                    }
+                }
+            }
+        }
+        _numActive++;
+        return obj;
+    }
+
+    /**
+     * <p>Returns an instance to the pool after successful validation and passivation. The returning instance
+     * is destroyed if any of the following are true:<ul>
+     *   <li>the pool is closed</li>
+     *   <li>{@link PoolableObjectFactory#validateObject(Object) validation} fails</li>
+     *   <li>{@link PoolableObjectFactory#passivateObject(Object) passivation} throws an exception</li>
+     * </ul>
+     *</p>
+     * 
+     * <p>Exceptions passivating or destroying instances are silently swallowed.  Exceptions validating
+     * instances are propagated to the client.</p>
+     * 
+     * @param obj instance to return to the pool
+     */
+    @Override
+    public synchronized void returnObject(T obj) throws Exception {
+        boolean success = !isClosed();
+        if (_factory != null) {
+            if(!_factory.validateObject(obj)) {
+                success = false;
+            } else {
+                try {
+                    _factory.passivateObject(obj);
+                } catch(Exception e) {
+                    success = false;
+                }
+            }
+        }
+
+        boolean shouldDestroy = !success;
+        _numActive--;
+        if(success) {
+            _pool.add(new SoftReference<T>(obj, refQueue));
+        }
+        notifyAll(); // _numActive has changed
+
+        if (shouldDestroy && _factory != null) {
+            try {
+                _factory.destroyObject(obj);
+            } catch(Exception e) {
+                // ignored
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void invalidateObject(T obj) throws Exception {
+        _numActive--;
+        if (_factory != null) {
+            _factory.destroyObject(obj);
+        }
+        notifyAll(); // _numActive has changed
+    }
+
+    /**
+     * <p>Create an object, and place it into the pool.
+     * addObject() is useful for "pre-loading" a pool with idle objects.</p>
+     * 
+     * <p>Before being added to the pool, the newly created instance is
+     * {@link PoolableObjectFactory#validateObject(Object) validated} and 
+     * {@link PoolableObjectFactory#passivateObject(Object) passivated}.  If validation
+     * fails, the new instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed}.
+     * Exceptions generated by the factory <code>makeObject</code> or <code>passivate</code> are
+     * propagated to the caller. Exceptions destroying instances are silently swallowed.</p>
+     * 
+     * @throws IllegalStateException if invoked on a {@link #close() closed} pool
+     * @throws Exception when the {@link #getFactory() factory} has a problem creating or passivating an object.
+     */
+    @Override
+    public synchronized void addObject() throws Exception {
+        assertOpen();
+        if (_factory == null) {
+            throw new IllegalStateException("Cannot add objects without a factory.");
+        }
+        T obj = _factory.makeObject();
+
+        boolean success = true;
+        if(!_factory.validateObject(obj)) {
+            success = false;
+        } else {
+            _factory.passivateObject(obj);
+        }
+
+        boolean shouldDestroy = !success;
+        if(success) {
+            _pool.add(new SoftReference<T>(obj, refQueue));
+            notifyAll(); // _numActive has changed
+        }
+
+        if(shouldDestroy) {
+            try {
+                _factory.destroyObject(obj);
+            } catch(Exception e) {
+                // ignored
+            }
+        }
+    }
+
+    /**
+     * Returns an approximation not less than the of the number of idle instances in the pool.
+     * 
+     * @return estimated number of idle instances in the pool
+     */
+    @Override
+    public synchronized int getNumIdle() {
+        pruneClearedReferences();
+        return _pool.size();
+    }
+
+    /**
+     * Return the number of instances currently borrowed from this pool.
+     *
+     * @return the number of instances currently borrowed from this pool
+     */
+    @Override
+    public synchronized int getNumActive() {
+        return _numActive;
+    }
+
+    /**
+     * Clears any objects sitting idle in the pool.
+     */
+    @Override
+    public synchronized void clear() {
+        if(null != _factory) {
+            Iterator<SoftReference<T>> iter = _pool.iterator();
+            while(iter.hasNext()) {
+                try {
+                    T obj = iter.next().get();
+                    if(null != obj) {
+                        _factory.destroyObject(obj);
+                    }
+                } catch(Exception e) {
+                    // ignore error, keep destroying the rest
+                }
+            }
+        }
+        _pool.clear();
+        pruneClearedReferences();
+    }
+
+    /**
+     * <p>Close this pool, and free any resources associated with it. Invokes
+     * {@link #clear()} to destroy and remove instances in the pool.</p>
+     * 
+     * <p>Calling {@link #addObject} or {@link #borrowObject} after invoking
+     * this method on a pool will cause them to throw an
+     * {@link IllegalStateException}.</p>
+     *
+     * @throws Exception never - exceptions clearing the pool are swallowed
+     */
+    @Override
+    public void close() throws Exception {
+        super.close();
+        clear();
+    }
+
+    /**
+     * Sets the {@link PoolableObjectFactory factory} this pool uses
+     * to create new instances. Trying to change
+     * the <code>factory</code> while there are borrowed objects will
+     * throw an {@link IllegalStateException}.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to create new instances.
+     * @throws IllegalStateException when the factory cannot be set at this time
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    @Override
+    public synchronized void setFactory(PoolableObjectFactory<T> factory) throws IllegalStateException {
+        assertOpen();
+        if(0 < getNumActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+
+    /**
+     * If any idle objects were garbage collected, remove their
+     * {@link Reference} wrappers from the idle object pool.
+     */
+    private void pruneClearedReferences() {
+        Reference<? extends T> ref;
+        while ((ref = refQueue.poll()) != null) {
+            try {
+                _pool.remove(ref);
+            } catch (UnsupportedOperationException uoe) {
+                // ignored
+            }
+        }
+    }
+    
+    /**
+     * Returns the {@link PoolableObjectFactory} used by this pool to create and manage object instances.
+     * 
+     * @return the factory
+     * @since 1.5.5
+     */
+    public synchronized PoolableObjectFactory<T> getFactory() { 
+        return _factory;
+    }
+
+    /** My pool. */
+    private final List<SoftReference<T>> _pool;
+
+    /** My {@link PoolableObjectFactory}. */
+    private PoolableObjectFactory<T> _factory = null;
+
+    /**
+     * Queue of broken references that might be able to be removed from <code>_pool</code>.
+     * This is used to help {@link #getNumIdle()} be more accurate with minimial
+     * performance overhead.
+     */
+    private final ReferenceQueue<T> refQueue = new ReferenceQueue<T>();
+
+    /** Number of active objects. */
+    private int _numActive = 0; //@GuardeBy("this")
+}
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..192b60e
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackKeyedObjectPool.java
@@ -0,0 +1,654 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+
+import org.apache.commons.pool.BaseKeyedObjectPool;
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+import org.apache.commons.pool.PoolUtils;
+
+/**
+ * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> 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 <code>Object</code>s without introducing
+ * artificial limits.
+ * </p>
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
+ * @see Stack
+ * @since Pool 1.0
+ */
+public class StackKeyedObjectPool<K, V> extends BaseKeyedObjectPool<K, V> implements KeyedObjectPool<K, V> {
+    /**
+     * Create a new pool using no factory.
+     * Clients must first set the {@link #setFactory factory} or
+     * may populate the pool using {@link #returnObject returnObject}
+     * before they can be {@link #borrowObject borrowed}.
+     *
+     * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
+     * @see #setFactory(KeyedPoolableObjectFactory)
+     */
+    public StackKeyedObjectPool() {
+        this(null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using no factory.
+     * Clients must first set the {@link #setFactory factory} or
+     * may populate the pool using {@link #returnObject returnObject}
+     * before they can be {@link #borrowObject borrowed}.
+     *
+     * @param max cap on the number of "sleeping" instances in the pool
+     * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
+     * @see #setFactory(KeyedPoolableObjectFactory)
+     */
+    public StackKeyedObjectPool(int max) {
+        this(null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using no factory.
+     * Clients must first set the {@link #setFactory factory} or
+     * may populate the pool using {@link #returnObject returnObject}
+     * before they can be {@link #borrowObject borrowed}.
+     *
+     * @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.)
+     * @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
+     * @see #setFactory(KeyedPoolableObjectFactory)
+     */
+    public StackKeyedObjectPool(int max, int init) {
+        this(null,max,init);
+    }
+
+    /**
+     * Create a new <code>SimpleKeyedObjectPool</code> using
+     * the specified <code>factory</code> to create new instances.
+     *
+     * @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
+     */
+    public StackKeyedObjectPool(KeyedPoolableObjectFactory<K, V> factory) {
+        this(factory,DEFAULT_MAX_SLEEPING);
+    }
+
+    /**
+     * Create a new <code>SimpleKeyedObjectPool</code> using
+     * the specified <code>factory</code> to create new instances.
+     * capping the number of "sleeping" instances to <code>max</code>
+     *
+     * @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<K, V> factory, int max) {
+        this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new <code>SimpleKeyedObjectPool</code> using
+     * the specified <code>factory</code> to create new instances.
+     * capping the number of "sleeping" instances to <code>max</code>,
+     * and initially allocating a container capable of containing
+     * at least <code>init</code> 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<K, V> 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<K, Stack<V>>();
+        _activeCount = new HashMap<K, Integer>();
+    }
+
+    /**
+     * Borrows an object with the given key.  If there are no idle instances under the
+     * given key, a new one is created.
+     * 
+     * @param key the pool key
+     * @return keyed poolable object instance
+     */
+    @Override
+    public synchronized V borrowObject(K key) throws Exception {
+        assertOpen();
+        Stack<V> stack = (_pools.get(key));
+        if(null == stack) {
+            stack = new Stack<V>();
+            stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
+            _pools.put(key,stack);
+        }
+        V obj = null;
+        do {
+            boolean newlyMade = false;
+            if (!stack.empty()) {
+                obj = stack.pop();
+                _totIdle--;
+            } else {
+                if(null == _factory) {
+                    throw new NoSuchElementException("pools without a factory cannot create new objects as needed.");
+                } else {
+                    obj = _factory.makeObject(key);
+                    newlyMade = true;
+                }
+            }
+            if (null != _factory && null != obj) {
+                try {
+                    _factory.activateObject(key, obj);
+                    if (!_factory.validateObject(key, obj)) {
+                        throw new Exception("ValidateObject failed");
+                    }
+                } catch (Throwable t) {
+                    PoolUtils.checkRethrow(t);
+                    try {
+                        _factory.destroyObject(key,obj);
+                    } catch (Throwable t2) {
+                        PoolUtils.checkRethrow(t2);
+                        // swallowed
+                    } finally {
+                        obj = null;
+                    }
+                    if (newlyMade) {
+                        throw new NoSuchElementException(
+                            "Could not create a validated object, cause: " +
+                            t.getMessage());
+                    }
+                }
+            }
+        } while (obj == null);
+        incrementActiveCount(key);
+        return obj;
+    }
+
+    /**
+     * Returns <code>obj</code> to the pool under <code>key</code>.  If adding the
+     * returning instance to the pool results in {@link #_maxSleeping maxSleeping}
+     * exceeded for the given key, the oldest instance in the idle object pool
+     * is destroyed to make room for the returning instance.
+     * 
+     * @param key the pool key
+     * @param obj returning instance
+     */
+    @Override
+    public synchronized void returnObject(K key, V obj) throws Exception {
+        decrementActiveCount(key);
+        if (null != _factory) {
+            if (_factory.validateObject(key, obj)) {
+                try {
+                    _factory.passivateObject(key, obj);
+                } catch (Exception ex) {
+                    _factory.destroyObject(key, obj);
+                    return;
+                }
+            } else {
+                return;
+            }
+        }
+
+        if (isClosed()) {
+            if (null != _factory) {
+                try {
+                    _factory.destroyObject(key, obj);
+                } catch (Exception e) {
+                    // swallowed
+                }
+            }
+            return;
+        }
+
+        Stack<V> stack = _pools.get(key);
+        if(null == stack) {
+            stack = new Stack<V>();
+            stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
+            _pools.put(key,stack);
+        }
+        final int stackSize = stack.size();
+        if (stackSize >= _maxSleeping) {
+            final V staleObj;
+            if (stackSize > 0) {
+                staleObj = stack.remove(0);
+                _totIdle--;
+            } else {
+                staleObj = obj;
+            }
+            if(null != _factory) {
+                try {
+                    _factory.destroyObject(key, staleObj);
+                } catch (Exception e) {
+                    // swallowed
+                }
+            }
+        }
+        stack.push(obj);
+        _totIdle++;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void invalidateObject(K key, V obj) throws Exception {
+        decrementActiveCount(key);
+        if(null != _factory) {
+            _factory.destroyObject(key,obj);
+        }
+        notifyAll(); // _totalActive has changed
+    }
+
+    /**
+     * Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
+     * passivate it, and then placed in the idle object pool.
+     * <code>addObject</code> is useful for "pre-loading" a pool with idle objects.
+     *
+     * @param key the key a new instance should be added to
+     * @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
+     * @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been called on this pool.
+     */
+    @Override
+    public synchronized void addObject(K key) throws Exception {
+        assertOpen();
+        if (_factory == null) {
+            throw new IllegalStateException("Cannot add objects without a factory.");
+        }
+        V obj = _factory.makeObject(key);
+        try {
+            if (!_factory.validateObject(key, obj)) {
+               return;
+            }
+        } catch (Exception e) {
+            try {
+                _factory.destroyObject(key, obj);
+            } catch (Exception e2) {
+                // swallowed
+            }
+            return;
+        }
+        _factory.passivateObject(key, obj);
+
+        Stack<V> stack = _pools.get(key);
+        if(null == stack) {
+            stack = new Stack<V>();
+            stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
+            _pools.put(key,stack);
+        }
+
+        final int stackSize = stack.size();
+        if (stackSize >= _maxSleeping) {
+            final V staleObj;
+            if (stackSize > 0) {
+                staleObj = stack.remove(0);
+                _totIdle--;
+            } else {
+                staleObj = obj;
+            }
+            try {
+                _factory.destroyObject(key, staleObj);
+            } catch (Exception e) {
+                // Don't swallow destroying the newly created object.
+                if (obj == staleObj) {
+                    throw e;
+                }
+            }
+        } else {
+            stack.push(obj);
+            _totIdle++;
+        }
+    }
+
+    /**
+     * Returns the total number of instances currently idle in this pool.
+     *
+     * @return the total number of instances currently idle in this pool
+     */
+    @Override
+    public synchronized int getNumIdle() {
+        return _totIdle;
+    }
+
+    /**
+     * Returns the total number of instances current borrowed from this pool but not yet returned.
+     *
+     * @return the total number of instances currently borrowed from this pool
+     */
+    @Override
+    public synchronized int getNumActive() {
+        return _totActive;
+    }
+
+    /**
+     * Returns the number of instances currently borrowed from but not yet returned
+     * to the pool corresponding to the given <code>key</code>.
+     *
+     * @param key the key to query
+     * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
+     */
+    @Override
+    public synchronized int getNumActive(K key) {
+        return getActiveCount(key);
+    }
+
+    /**
+     * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
+     *
+     * @param key the key to query
+     * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
+     */
+    @Override
+    public synchronized int getNumIdle(K key) {
+        try {
+            return(_pools.get(key)).size();
+        } catch(Exception e) {
+            return 0;
+        }
+    }
+
+    /**
+     * Clears the pool, removing all pooled instances.
+     */
+    @Override
+    public synchronized void clear() {
+        Iterator<K> it = _pools.keySet().iterator();
+        while(it.hasNext()) {
+            K key = it.next();
+            Stack<V> stack = _pools.get(key);
+            destroyStack(key,stack);
+        }
+        _totIdle = 0;
+        _pools.clear();
+        _activeCount.clear();
+    }
+
+    /**
+     * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
+     *
+     * @param key the key to clear
+     */
+    @Override
+    public synchronized void clear(K key) {
+        Stack<V> stack = _pools.remove(key);
+        destroyStack(key,stack);
+    }
+
+    /**
+     * Destroys all instances in the stack and clears the stack.
+     * 
+     * @param key key passed to factory when destroying instances
+     * @param stack stack to destroy
+     */
+    private synchronized void destroyStack(K key, Stack<V> stack) {
+        if(null == stack) {
+            return;
+        } else {
+            if(null != _factory) {
+                Iterator<V> it = stack.iterator();
+                while(it.hasNext()) {
+                    try {
+                        _factory.destroyObject(key,it.next());
+                    } catch(Exception e) {
+                        // ignore error, keep destroying the rest
+                    }
+                }
+            }
+            _totIdle -= stack.size();
+            _activeCount.remove(key);
+            stack.clear();
+        }
+    }
+
+    /**
+     * Returns a string representation of this StackKeyedObjectPool, including
+     * the number of pools, the keys and the size of each keyed pool.
+     * 
+     * @return Keys and pool sizes
+     */
+    @Override
+    public synchronized String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append(getClass().getName());
+        buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
+        Iterator<K> it = _pools.keySet().iterator();
+        while(it.hasNext()) {
+            K key = it.next();
+            buf.append(" |").append(key).append("|=");
+            Stack<V> s = _pools.get(key);
+            buf.append(s.size());
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Close this pool, and free any resources associated with it.
+     * <p>
+     * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking
+     * this method on a pool will cause them to throw an {@link IllegalStateException}.
+     * </p>
+     *
+     * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed.
+     */
+    @Override
+    public void close() throws Exception {
+        super.close();
+        clear();
+    }
+
+    /**
+     * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses
+     * to create new instances.
+     * Trying to change the <code>factory</code> after a pool has been used will frequently
+     * throw an {@link UnsupportedOperationException}.
+     *
+     * @param factory the {@link KeyedPoolableObjectFactory} used to manage object instances
+     * @throws IllegalStateException when the factory cannot be set at this time
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    @Override
+    public synchronized void setFactory(KeyedPoolableObjectFactory<K, V> factory) throws IllegalStateException {
+        if(0 < getNumActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+    
+    /**
+     * @return the {@link KeyedPoolableObjectFactory} used by this pool to manage object instances.
+     * @since 1.5.5
+     */
+    public synchronized KeyedPoolableObjectFactory<K, V> getFactory() {
+        return _factory;
+    }
+
+    /**
+     * Returns the active instance count for the given key.
+     * 
+     * @param key pool key
+     * @return active count
+     */
+    private int getActiveCount(K key) {
+        try {
+            return _activeCount.get(key).intValue();
+        } catch(NoSuchElementException e) {
+            return 0;
+        } catch(NullPointerException e) {
+            return 0;
+        }
+    }
+
+    /**
+     * Increment the active count for the given key. Also
+     * increments the total active count.
+     * 
+     * @param key pool key
+     */
+    private void incrementActiveCount(K key) {
+        _totActive++;
+        Integer old = _activeCount.get(key);
+        if(null == old) {
+            _activeCount.put(key,new Integer(1));
+        } else {
+            _activeCount.put(key,new Integer(old.intValue() + 1));
+        }
+    }
+
+    /**
+     * Decrements the active count for the given key.
+     * Also decrements the total active count.
+     * 
+     * @param key pool key
+     */
+    private void decrementActiveCount(K key) {
+        _totActive--;
+        Integer active = _activeCount.get(key);
+        if(null == active) {
+            // do nothing, either null or zero is OK
+        } else if(active.intValue() <= 1) {
+            _activeCount.remove(key);
+        } else {
+            _activeCount.put(key, new Integer(active.intValue() - 1));
+        }
+    }
+
+    
+    /**
+     * @return map of keyed pools
+     * @since 1.5.5
+     */
+    public Map<K, Stack<V>> getPools() {
+        return _pools;
+    }
+
+    /**
+     * @return the cap on the number of "sleeping" instances in <code>each</code> pool.
+     * @since 1.5.5
+     */
+    public int getMaxSleeping() {
+        return _maxSleeping;
+    }
+
+    /**
+     * @return the initial capacity of each pool.
+     * @since 1.5.5
+     */
+    public int getInitSleepingCapacity() {
+        return _initSleepingCapacity;
+    }
+
+    /**
+     * @return the _totActive
+     */
+    public int getTotActive() {
+        return _totActive;
+    }
+
+    /**
+     * @return the _totIdle
+     */
+    public int getTotIdle() {
+        return _totIdle;
+    }
+
+    /**
+     * @return the _activeCount
+     * @since 1.5.5
+     */
+    public Map<K, Integer> getActiveCount() {
+        return _activeCount;
+    }
+
+
+    /** 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.
+     *  @deprecated to be removed in pool 2.0.  Use {@link #getPools()}
+     */
+    @Deprecated
+    protected HashMap<K, Stack<V>> _pools = null;
+
+    /**
+     * My {@link KeyedPoolableObjectFactory}.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getFactory()}
+     */
+    @Deprecated
+    protected KeyedPoolableObjectFactory<K, V> _factory = null;
+
+    /**
+     *  The cap on the number of "sleeping" instances in <code>each</code> pool.
+     *  @deprecated to be removed in pool 2.0.  Use {@link #getMaxSleeping()}
+     */
+    @Deprecated
+    protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
+
+    /**
+     * The initial capacity of each pool.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getInitSleepingCapacity()}.
+     */
+    @Deprecated
+    protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
+
+    /**
+     * Total number of object borrowed and not yet returned for all pools.
+     * @deprecated to be removed in pool 2.0.  Use {@link #getTotActive()}.
+     */
+    @Deprecated
+    protected int _totActive = 0;
+
+    /**
+     * Total number of objects "sleeping" for all pools
+     * @deprecated to be removed in pool 2.0.  Use {@link #getTotIdle()}.
+     */
+    @Deprecated
+    protected int _totIdle = 0;
+
+    /**
+     * Number of active objects borrowed and not yet returned by pool
+     * @deprecated to be removed in pool 2.0.  Use {@link #getActiveCount()}.
+     */
+    @Deprecated
+    protected HashMap<K, Integer> _activeCount = null;
+
+}
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..c02b053
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackKeyedObjectPoolFactory.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedObjectPoolFactory;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+
+/**
+ * A factory for creating {@link StackKeyedObjectPool} instances.
+ *
+ * @param <K> the type of keys in this pool
+ * @param <V> the type of objects held in this pool
+ * 
+ * @see StackKeyedObjectPool
+ * @see KeyedObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class StackKeyedObjectPoolFactory<K, V> implements KeyedObjectPoolFactory<K, V> {
+    /**
+     * Create a new StackKeyedObjectPoolFactory.
+     *
+     * @see StackKeyedObjectPool#StackKeyedObjectPool()
+     */
+    public StackKeyedObjectPoolFactory() {
+        this(null,StackKeyedObjectPool.DEFAULT_MAX_SLEEPING,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackKeyedObjectPoolFactory.
+     *
+     * @param maxSleeping cap on the number of "sleeping" instances in the pool.
+     * @see StackKeyedObjectPool#StackKeyedObjectPool(int)
+     */
+    public StackKeyedObjectPoolFactory(int maxSleeping) {
+        this(null,maxSleeping,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackKeyedObjectPoolFactory.
+     *
+     * @param maxSleeping cap on the number of "sleeping" instances in the pool.
+     * @param initialCapacity initial size of the pool (this specifies the size of the container,
+     * it does not cause the pool to be pre-populated.)
+     * @see StackKeyedObjectPool#StackKeyedObjectPool(int, int)
+     */
+    public StackKeyedObjectPoolFactory(int maxSleeping, int initialCapacity) {
+        this(null,maxSleeping,initialCapacity);
+    }
+
+    /**
+     * Create a new StackKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory used by created pools.
+     * @see StackKeyedObjectPool#StackKeyedObjectPool(KeyedPoolableObjectFactory)
+     */
+    public StackKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory) {
+        this(factory,StackKeyedObjectPool.DEFAULT_MAX_SLEEPING,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory used by created pools.
+     * @param maxSleeping cap on the number of "sleeping" instances in the pool.
+     * @see StackKeyedObjectPool#StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
+     */
+    public StackKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxSleeping) {
+        this(factory,maxSleeping,StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackKeyedObjectPoolFactory.
+     *
+     * @param factory the KeyedPoolableObjectFactory used by created pools.
+     * @param maxSleeping cap on the number of "sleeping" instances in the pool.
+     * @param initialCapacity initial size of the pool (this specifies the size of the container,
+     * it does not cause the pool to be pre-populated.)
+     * @see StackKeyedObjectPool#StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
+     */
+    public StackKeyedObjectPoolFactory(KeyedPoolableObjectFactory<K, V> factory, int maxSleeping, int initialCapacity) {
+        _factory = factory;
+        _maxSleeping = maxSleeping;
+        _initCapacity = initialCapacity;
+    }
+
+    /**
+     * Create a StackKeyedObjectPool with current property settings.
+     * 
+     * @return a new StackKeyedObjectPool with the configured factory, maxSleeping and initialCapacity
+     */
+    public KeyedObjectPool<K, V> createPool() {
+        return new StackKeyedObjectPool<K, V>(_factory,_maxSleeping,_initCapacity);
+    }
+
+    /** 
+     * KeyedPoolableObjectFactory used by StackKeyedObjectPools created by this factory
+     * @deprecated to be removed in pool 2.0 
+     */
+    @Deprecated
+    protected KeyedPoolableObjectFactory<K, V> _factory = null;
+    
+    /** 
+     * Maximum number of idle instances in each keyed pool for StackKeyedObjectPools created by this factory
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    protected int _maxSleeping = StackKeyedObjectPool.DEFAULT_MAX_SLEEPING;
+    
+    /**
+     * Initial capacity of StackKeyedObjectPools created by this factory.
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    protected int _initCapacity = StackKeyedObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY;
+
+    /**
+     * Returns the KeyedPoolableObjectFactory used by StackKeyedObjectPools created by this factory
+     * 
+     * @return factory setting for created pools
+     * @since 1.5.5
+     */
+    public KeyedPoolableObjectFactory<K, V> getFactory() {
+        return _factory;
+    }
+
+    /**
+     * Returns the maximum number of idle instances in each keyed pool for StackKeyedObjectPools created by this factory
+     * 
+     * @return maxSleeping setting for created pools
+     * @since 1.5.5
+     */
+    public int getMaxSleeping() {
+        return _maxSleeping;
+    }
+
+    /**
+     * Returns the initial capacity of StackKeyedObjectPools created by this factory.
+     * 
+     * @return initial capacity setting for created pools
+     * @since 1.5.5
+     */
+    public int getInitialCapacity() {
+        return _initCapacity;
+    }
+
+}
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..89b27eb
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackObjectPool.java
@@ -0,0 +1,471 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+
+import org.apache.commons.pool.BaseObjectPool;
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolUtils;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+/**
+ * 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.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @author Rodney Waldhoff
+ * @author Dirk Verbeeck
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class StackObjectPool<T> extends BaseObjectPool<T> implements ObjectPool<T> {
+    /**
+     * Create a new pool using no factory. Clients must first 
+     * {@link #setFactory(PoolableObjectFactory) set the factory} or
+     * else this pool will not behave correctly. Clients may first populate the pool
+     * using {@link #returnObject(java.lang.Object)} before they can be {@link #borrowObject borrowed}
+     * but this usage is <strong>discouraged</strong>.
+     *
+     * @see #StackObjectPool(PoolableObjectFactory)
+     * @deprecated to be removed in pool 2.0 - use {@link #StackObjectPool(PoolableObjectFactory)}
+     */
+    @Deprecated
+    public StackObjectPool() {
+        this(null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using no factory.
+     * Clients must first {@link #setFactory(PoolableObjectFactory) set the factory} or
+     * else this pool will not behave correctly. Clients may first populate the pool
+     * using {@link #returnObject(java.lang.Object)} before they can be {@link #borrowObject borrowed}
+     * but this usage is <strong>discouraged</strong>.
+     *
+     * @param maxIdle cap on the number of "sleeping" instances in the pool
+     * @see #StackObjectPool(PoolableObjectFactory, int)
+     * @deprecated to be removed in pool 2.0 - use {@link #StackObjectPool(PoolableObjectFactory, int)}
+     */
+    @Deprecated
+    public StackObjectPool(int maxIdle) {
+        this(null,maxIdle,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new pool using no factory.
+     * Clients must first {@link #setFactory(PoolableObjectFactory) set the factory} or
+     * else this pool will not behave correctly. Clients may first populate the pool
+     * using {@link #returnObject(java.lang.Object)} before they can be {@link #borrowObject borrowed}
+     * but this usage is <strong>discouraged</strong>.
+     *
+     * @param maxIdle cap on the number of "sleeping" instances in the pool
+     * @param initIdleCapacity initial size of the pool (this specifies the size of the container,
+     *             it does not cause the pool to be pre-populated.)
+     * @see #StackObjectPool(PoolableObjectFactory, int, int)
+     * @deprecated to be removed in pool 2.0 - use {@link #StackObjectPool(PoolableObjectFactory, int, int)}
+     */
+    @Deprecated
+    public StackObjectPool(int maxIdle, int initIdleCapacity) {
+        this(null,maxIdle,initIdleCapacity);
+    }
+
+    /**
+     * 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<T> 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>maxIdle</i>.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to populate the pool
+     * @param maxIdle cap on the number of "sleeping" instances in the pool
+     */
+    public StackObjectPool(PoolableObjectFactory<T> factory, int maxIdle) {
+        this(factory,maxIdle,DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * <p>Create a new <tt>StackObjectPool</tt> using the specified <code>factory</code> to create new instances,
+     * capping the number of "sleeping" instances to <code>maxIdle</code>, and initially allocating a container
+     * capable of containing at least <code>initIdleCapacity</code> instances.  The pool is not pre-populated.
+     * The <code>initIdleCapacity</code> parameter just determines the initial size of the underlying
+     * container, which can increase beyond this value if <code>maxIdle > initIdleCapacity.</code></p>
+     * 
+     * <p>Negative values of <code>maxIdle</code> are ignored (i.e., the pool is created using
+     * {@link #DEFAULT_MAX_SLEEPING}) as are non-positive values for <code>initIdleCapacity.</code>
+     *
+     * @param factory the {@link PoolableObjectFactory} used to populate the pool
+     * @param maxIdle cap on the number of "sleeping" instances in the pool
+     * @param initIdleCapacity 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<T> factory, int maxIdle, int initIdleCapacity) {
+        _factory = factory;
+        _maxSleeping = (maxIdle < 0 ? DEFAULT_MAX_SLEEPING : maxIdle);
+        int initcapacity = (initIdleCapacity < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : initIdleCapacity);
+        _pool = new Stack<T>();
+        _pool.ensureCapacity( initcapacity > _maxSleeping ? _maxSleeping : initcapacity);
+    }
+
+    /**
+     * <p>Borrows an object from the pool.  If there are idle instances available on the stack,
+     * the top element of the stack is popped to activate, validate and return to the client.  If there
+     * are no idle instances available, the {@link PoolableObjectFactory#makeObject() makeObject} 
+     * method of the pool's {@link PoolableObjectFactory} is invoked to create a new instance.</p>
+     * 
+     * <p>All instances are {@link PoolableObjectFactory#activateObject(Object) activated} and
+     * {@link PoolableObjectFactory#validateObject(Object) validated} before being returned to the
+     * client.  If validation fails or an exception occurs activating or validating an instance 
+     * popped from the idle instance stack, the failing instance is 
+     * {@link PoolableObjectFactory#destroyObject(Object) destroyed} and the next instance on
+     * the stack is popped, validated and activated.  This process continues until either the
+     * stack is empty or an instance passes validation.  If the stack is empty on activation or
+     * it does not contain any valid instances, the factory's <code>makeObject</code> method is used
+     * to create a new instance.  If a null instance is returned by the factory or the created
+     * instance either raises an exception on activation or fails validation, <code>NoSuchElementException</code>
+     * is thrown. Exceptions thrown by <code>MakeObject</code> are propagated to the caller; but 
+     * other than <code>ThreadDeath</code> or <code>VirtualMachineError</code>, exceptions generated by
+     * activation, validation or destroy methods are swallowed silently.</p>
+     * 
+     * @return an instance from the pool
+     */
+    @Override
+    public synchronized T borrowObject() throws Exception {
+        assertOpen();
+        T obj = null;
+        boolean newlyCreated = false;
+        while (null == obj) {
+            if (!_pool.empty()) {
+                obj = _pool.pop();
+            } else {
+                if(null == _factory) {
+                    throw new NoSuchElementException();
+                } else {
+                    obj = _factory.makeObject();
+                    newlyCreated = true;
+                  if (obj == null) {
+                    throw new NoSuchElementException("PoolableObjectFactory.makeObject() returned null.");
+                  }
+                }
+            }
+            if (null != _factory && null != obj) {
+                try {
+                    _factory.activateObject(obj);
+                    if (!_factory.validateObject(obj)) {
+                        throw new Exception("ValidateObject failed");
+                    }
+                } catch (Throwable t) {
+                    PoolUtils.checkRethrow(t);
+                    try {
+                        _factory.destroyObject(obj);
+                    } catch (Throwable t2) {
+                        PoolUtils.checkRethrow(t2);
+                        // swallowed
+                    } finally {
+                        obj = null;
+                    }
+                    if (newlyCreated) {
+                        throw new NoSuchElementException(
+                            "Could not create a validated object, cause: " +
+                            t.getMessage());
+                    }
+                }
+            }
+        }
+        _numActive++;
+        return obj;
+    }
+
+    /**
+     * <p>Returns an instance to the pool, pushing it on top of the idle instance stack after successful
+     * validation and passivation. The returning instance is destroyed if any of the following are true:<ul>
+     *   <li>the pool is closed</li>
+     *   <li>{@link PoolableObjectFactory#validateObject(Object) validation} fails</li>
+     *   <li>{@link PoolableObjectFactory#passivateObject(Object) passivation} throws an exception</li>
+     * </ul>
+     * If adding a validated, passivated returning instance to the stack would cause
+     * {@link #getMaxSleeping() maxSleeping} to be exceeded, the oldest (bottom) instance on the stack
+     * is destroyed to make room for the returning instance, which is pushed on top of the stack.</p>
+     * 
+     * <p>Exceptions passivating or destroying instances are silently swallowed.  Exceptions validating
+     * instances are propagated to the client.</p>
+     * 
+     * @param obj instance to return to the pool
+     */
+    @Override
+    public synchronized void returnObject(T obj) throws Exception {
+        boolean success = !isClosed();
+        if(null != _factory) {
+            if(!_factory.validateObject(obj)) {
+                success = false;
+            } else {
+                try {
+                    _factory.passivateObject(obj);
+                } catch(Exception e) {
+                    success = false;
+                }
+            }
+        }
+
+        boolean shouldDestroy = !success;
+
+        _numActive--;
+        if (success) {
+            T toBeDestroyed = null;
+            if(_pool.size() >= _maxSleeping) {
+                shouldDestroy = true;
+                toBeDestroyed = _pool.remove(0); // remove the stalest object
+            }
+            _pool.push(obj);
+            obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed
+        }
+        notifyAll(); // _numActive has changed
+
+        if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
+            try {
+                _factory.destroyObject(obj);
+            } catch(Exception e) {
+                // ignored
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void invalidateObject(T obj) throws Exception {
+        _numActive--;
+        if (null != _factory) {
+            _factory.destroyObject(obj);
+        }
+        notifyAll(); // _numActive has changed
+    }
+
+    /**
+     * Return the number of instances
+     * currently idle in this pool.
+     *
+     * @return the number of instances currently idle in this pool
+     */
+    @Override
+    public synchronized int getNumIdle() {
+        return _pool.size();
+    }
+
+    /**
+     * Return the number of instances currently borrowed from this pool.
+     *
+     * @return the number of instances currently borrowed from this pool
+     */
+    @Override
+    public synchronized int getNumActive() {
+        return _numActive;
+    }
+
+    /**
+     * Clears any objects sitting idle in the pool. Silently swallows any
+     * exceptions thrown by {@link PoolableObjectFactory#destroyObject(Object)}.
+     */
+    @Override
+    public synchronized void clear() {
+        if(null != _factory) {
+            Iterator<T> it = _pool.iterator();
+            while(it.hasNext()) {
+                try {
+                    _factory.destroyObject(it.next());
+                } catch(Exception e) {
+                    // ignore error, keep destroying the rest
+                }
+            }
+        }
+        _pool.clear();
+    }
+
+    /**
+     * <p>Close this pool, and free any resources associated with it. Invokes
+     * {@link #clear()} to destroy and remove instances in the pool.</p>
+     * 
+     * <p>Calling {@link #addObject} or {@link #borrowObject} after invoking
+     * this method on a pool will cause them to throw an
+     * {@link IllegalStateException}.</p>
+     *
+     * @throws Exception never - exceptions clearing the pool are swallowed
+     */
+    @Override
+    public void close() throws Exception {
+        super.close();
+        clear();
+    }
+
+    /**
+     * <p>Create an object, and place it on top of the stack.
+     * This method is useful for "pre-loading" a pool with idle objects.</p>
+     * 
+     * <p>Before being added to the pool, the newly created instance is
+     * {@link PoolableObjectFactory#validateObject(Object) validated} and 
+     * {@link PoolableObjectFactory#passivateObject(Object) passivated}.  If validation
+     * fails, the new instance is {@link PoolableObjectFactory#destroyObject(Object) destroyed}.
+     * Exceptions generated by the factory <code>makeObject</code> or <code>passivate</code> are
+     * propagated to the caller. Exceptions destroying instances are silently swallowed.</p>
+     * 
+     * <p>If a new instance is created and successfully validated and passivated and adding this
+     * instance to the pool causes {@link #getMaxSleeping() maxSleeping} to be exceeded, the oldest
+     * (bottom) instance in the pool is destroyed to make room for the newly created instance, which
+     * is pushed on top of the stack.
+     * 
+     * @throws Exception when the {@link #getFactory() factory} has a problem creating or passivating an object.
+     */
+    @Override
+    public synchronized void addObject() throws Exception {
+        assertOpen();
+        if (_factory == null) {
+            throw new IllegalStateException("Cannot add objects without a factory.");
+        }
+        T obj = _factory.makeObject();
+
+        boolean success = true;
+        if(!_factory.validateObject(obj)) {
+            success = false;
+        } else {
+            _factory.passivateObject(obj);
+        }
+
+        boolean shouldDestroy = !success;
+
+        if (success) {
+            T toBeDestroyed = null;
+            if(_pool.size() >= _maxSleeping) {
+                shouldDestroy = true;
+                toBeDestroyed = _pool.remove(0); // remove the stalest object
+            }
+            _pool.push(obj);
+            obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed
+        }
+        notifyAll(); // _numIdle has changed
+
+        if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
+            try {
+                _factory.destroyObject(obj);
+            } catch(Exception e) {
+                // ignored
+            }
+        }
+    }
+
+    /**
+     * Sets the {@link PoolableObjectFactory factory} this pool uses
+     * to create new instances. Trying to change
+     * the <code>factory</code> while there are borrowed objects will
+     * throw an {@link IllegalStateException}.
+     *
+     * @param factory the {@link PoolableObjectFactory} used to create new instances.
+     * @throws IllegalStateException when the factory cannot be set at this time
+     * @deprecated to be removed in pool 2.0
+     */
+    @Deprecated
+    @Override
+    public synchronized void setFactory(PoolableObjectFactory<T> factory) throws IllegalStateException {
+        assertOpen();
+        if(0 < getNumActive()) {
+            throw new IllegalStateException("Objects are already active");
+        } else {
+            clear();
+            _factory = factory;
+        }
+    }
+
+    /**
+     * The 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.
+     * @deprecated to be made private in pool 2.0 
+     */
+    @Deprecated
+    protected Stack<T> _pool = null;
+
+    /** 
+     * My {@link PoolableObjectFactory}.
+     * @deprecated to be made private in pool 2.0 - use {@link #getFactory()}
+     */
+    @Deprecated
+    protected PoolableObjectFactory<T> _factory = null;
+
+    /** 
+     * The cap on the number of "sleeping" instances in the pool. 
+     * @deprecated to be made private in pool 2.0 - use {@link #getMaxSleeping()}
+     */
+    @Deprecated
+    protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
+    
+    /**
+     * Number of objects borrowed but not yet returned to the pool.
+     * @deprecated to be made private in pool 2.0 - use {@link #getNumActive()}
+     */
+    @Deprecated
+    protected int _numActive = 0;
+
+    /**
+     * Returns the {@link PoolableObjectFactory} used by this pool to create and manage object instances.
+     * 
+     * @return the factory
+     * @since 1.5.5
+     */
+    public synchronized PoolableObjectFactory<T> getFactory() {
+        return _factory;
+    }
+
+    /**
+     * Returns the maximum number of idle instances in the pool.
+     * 
+     * @return maxSleeping
+     * @since 1.5.5
+     */
+    public int getMaxSleeping() {
+        return _maxSleeping;
+    }
+
+   
+}
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..a8c07bf
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/StackObjectPoolFactory.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.ObjectPoolFactory;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+/**
+ * A factory for creating {@link StackObjectPool} instances.
+ *
+ * @param <T> the type of objects held in this pool
+ * 
+ * @see StackObjectPool
+ * @see StackKeyedObjectPoolFactory
+ *
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ * @since Pool 1.0
+ */
+public class StackObjectPoolFactory<T> implements ObjectPoolFactory<T> {
+    /**
+     * Create a new StackObjectPoolFactory.
+     *
+     * @see StackObjectPool#StackObjectPool()
+     * @deprecated to be removed in pool 2.0 - use {@link #StackObjectPoolFactory(PoolableObjectFactory)}
+     */
+    @Deprecated
+    public StackObjectPoolFactory() {
+        this(null,StackObjectPool.DEFAULT_MAX_SLEEPING,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackObjectPoolFactory.
+     *
+     * @param maxIdle cap on the number of "sleeping" instances in the pool.
+     * @see StackObjectPool#StackObjectPool(int)
+     * @deprecated to be removed in pool 2.0 - use {@link #StackObjectPoolFactory(PoolableObjectFactory, int)}
+     */
+    @Deprecated
+    public StackObjectPoolFactory(int maxIdle) {
+        this(null,maxIdle,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackObjectPoolFactory.
+     *
+     * @param maxIdle cap on the number of "sleeping" instances in the pool.
+     * @param initIdleCapacity - initial size of the pool (this specifies the size of the container,
+     * it does not cause the pool to be pre-populated.)
+     * @see StackObjectPool#StackObjectPool(int, int)
+     * @deprecated to be removed in pool 2.0 - use {@link #StackObjectPoolFactory(PoolableObjectFactory, int, int)}
+     */
+    @Deprecated
+    public StackObjectPoolFactory(int maxIdle, int initIdleCapacity) {
+        this(null,maxIdle,initIdleCapacity);
+    }
+
+    /**
+     * Create a new StackObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @see StackObjectPool#StackObjectPool(PoolableObjectFactory)
+     */
+    public StackObjectPoolFactory(PoolableObjectFactory<T> factory) {
+        this(factory,StackObjectPool.DEFAULT_MAX_SLEEPING,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxIdle cap on the number of "sleeping" instances in the pool.
+     */
+    public StackObjectPoolFactory(PoolableObjectFactory<T> factory, int maxIdle) {
+        this(factory,maxIdle,StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY);
+    }
+
+    /**
+     * Create a new StackObjectPoolFactory.
+     *
+     * @param factory the PoolableObjectFactory used by created pools.
+     * @param maxIdle cap on the number of "sleeping" instances in the pool.
+     * @param initIdleCapacity - initial size of the pool (this specifies the size of the container,
+     * it does not cause the pool to be pre-populated.)
+     */
+    public StackObjectPoolFactory(PoolableObjectFactory<T> factory, int maxIdle, int initIdleCapacity) {
+        _factory = factory;
+        _maxSleeping = maxIdle;
+        _initCapacity = initIdleCapacity;
+    }
+
+    /**
+     * Create a StackObjectPool.
+     * 
+     * @return a new StackObjectPool with the configured factory, maxIdle and initial capacity settings
+     */
+    public ObjectPool<T> createPool() {
+        return new StackObjectPool<T>(_factory,_maxSleeping,_initCapacity);
+    }
+
+    /**
+     * The PoolableObjectFactory used by created pools.
+     * @deprecated to be made private in pool 2.0
+     */
+    @Deprecated
+    protected PoolableObjectFactory<T> _factory = null;
+    
+    /**
+     * The maximum number of idle instances in created pools.
+     * @deprecated to be made private in pool 2.0
+     */
+    @Deprecated
+    protected int _maxSleeping = StackObjectPool.DEFAULT_MAX_SLEEPING;
+    
+    /**
+     * The initial size of created pools.
+     * @deprecated to be made private in pool 2.0
+     */
+    @Deprecated
+    protected int _initCapacity = StackObjectPool.DEFAULT_INIT_SLEEPING_CAPACITY;
+
+    /**
+     * Returns the factory used by created pools.
+     * 
+     * @return the PoolableObjectFactory used by created pools
+     * @since 1.5.5
+     */
+    public PoolableObjectFactory<T> getFactory() {
+        return _factory;
+    }
+
+    /**
+     * Returns the maxIdle setting for created pools.
+     * 
+     * @return the maximum number of idle instances in created pools
+     * @since 1.5.5
+     */
+    public int getMaxSleeping() {
+        return _maxSleeping;
+    }
+
+    /**
+     * Returns the initial capacity of created pools.
+     * 
+     * @return size of created containers (created pools are not pre-populated)
+     * @since 1.5.5
+     */
+    public int getInitCapacity() {
+        return _initCapacity;
+    }
+
+}
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..ee7f8dd
--- /dev/null
+++ b/src/java/org/apache/commons/pool/impl/package.html
@@ -0,0 +1,56 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id: package.html 777744 2009-05-22 23:42:26Z sebb $ -->
+<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/overview.html b/src/java/org/apache/commons/pool/overview.html
new file mode 100644
index 0000000..ab53d50
--- /dev/null
+++ b/src/java/org/apache/commons/pool/overview.html
@@ -0,0 +1,121 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id: overview.html 777748 2009-05-23 00:00:44Z sebb $ -->
+<html>
+   <head>
+      <title>Overview of the org.apache.commons.pool component</title>
+   </head>
+   <body>
+      <p>
+         Generic Object pooling API with several implementations.
+      </p>
+      <p>
+         The <code>org.apache.commons.pool</code> package defines a simple
+         interface for a pool of object instances, and a handful of base
+         classes that may be useful when creating pool implementations.
+         The API supports pooling of unique objects which can be requested
+         via a key as well as pools where all objects are equivalent.
+      </p>
+      <p>
+         The <code>org.apache.commons.pool.impl</code> package contains
+         several pool implementations.
+         {@link org.apache.commons.pool.impl.StackObjectPool StackObjectPool}
+         is useful for supporting reuse of a limited number of instances while
+         allowing new instances to be created as needed to support high demand.
+         {@link org.apache.commons.pool.impl.GenericObjectPool
+         GenericObjectPool} has many configuration options and can support
+         a limited set of objects such as would be useful in a database
+         connection pool.
+         {@link org.apache.commons.pool.impl.SoftReferenceObjectPool
+         SoftReferenceObjectPool} has no limit on the number of objects in the
+         pool, but the garbage collector can remove idle objects from the pool
+         as needed.  There are also keyed versions of the first two.
+      </p>
+      <p>
+         Here is a simple example of pooling <code>HashMap</code>'s. First
+         create an {@link org.apache.commons.pool.ObjectPoolFactory
+         ObjectPoolFactory}
+      </p>
+<pre>
+    public class HashMapFactory
+        extends {@link org.apache.commons.pool.BasePoolableObjectFactory BasePoolableObjectFactory}
+    {
+        /**
+         * Creates an instance that can be returned by the pool.
+         * @return an instance that can be returned by the pool.
+         */
+        public Object makeObject()
+            throws Exception
+        {
+            return new HashMap();
+        }
+
+        /**
+         * Uninitialize an instance to be returned to the pool.
+         * @param obj the instance to be passivated
+         */
+        public void passivateObject(Object obj)
+            throws Exception
+        {
+            Map map = (Map)obj;
+            map.clear();
+        }
+    }
+</pre>
+      <p>
+         A class that makes frequent use of a Map could then use a pool
+         as shown below:
+      </p>
+<pre>
+    public class Foo
+    {
+        private {@link org.apache.commons.pool.ObjectPool ObjectPool} pool;
+        public Foo()
+        {
+            {@link org.apache.commons.pool.PoolableObjectFactory PoolableObjectFactory} factory = new HashMapFactory();
+            pool = new StackObjectPool(factory, 1000);
+        }
+
+        public doSomething()
+        {
+            ...
+            Map map = null;
+            try
+            {
+                map = (Map)pool.borrowObject();
+                // use map
+                ...
+            }
+            finally
+            {
+                if (map != null)
+                {
+                    pool.returnObject(map);
+                }
+            }
+            ...
+        }
+    }
+</pre>
+
+<p>
+The above example shows how one would use an
+{@link org.apache.commons.pool.ObjectPool ObjectPool}.  The other supplied
+implementations or another special purpose pool would be used similarly.
+</p>
+   </body>
+</html>
\ No newline at end of file
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..419a7b8
--- /dev/null
+++ b/src/java/org/apache/commons/pool/package.html
@@ -0,0 +1,69 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id: package.html 777748 2009-05-23 00:00:44Z sebb $ -->
+<html>
+   <head>
+      <title>Package Documentation for org.apache.commons.pool</title>
+   </head>
+   <body>
+      <p>
+         Object pooling API.
+      </p>
+      <p>
+         The <code>org.apache.commons.pool</code> package defines a simple
+         interface for a pool of object instances, and a handful of base
+         classes that may be useful when creating pool implementations.
+      </p>
+      <p>
+         The <code>pool</code> package itself doesn't define a specific object
+         pooling implementation, but rather a contract that implementations may
+         support in order to be fully interchangeable.
+      </p>
+      <p>
+         The <code>pool</code> package separates the way in which instances are
+         pooled from the way in which they are created, resulting in a pair of
+         interfaces:
+      </p>
+      <dl>
+        <dt>{@link org.apache.commons.pool.ObjectPool ObjectPool}</dt>
+        <dd>
+          defines a simple object pooling interface, with methods for
+          borrowing instances from and returning them to the pool.
+        </dd>
+        <dt>{@link org.apache.commons.pool.PoolableObjectFactory PoolableObjectFactory}</dt>
+        <dd>
+          defines lifecycle methods for object instances contained within a pool.
+          By associating a factory with a pool, the pool can create new object
+          instances as needed.
+        </dd>
+      </dl>
+      <p>
+        {@link org.apache.commons.pool.ObjectPoolFactory ObjectPoolFactory}
+        defines a simple factory for <code>ObjectPool</code>s, which may be
+        useful for some applications.
+      </p>
+      <p>
+         The <code>pool</code> package also provides a keyed pool interface,
+         which pools instances of multiple types, accessed according to an
+         arbitrary key. See
+         {@link org.apache.commons.pool.KeyedObjectPool KeyedObjectPool},
+         {@link org.apache.commons.pool.KeyedPoolableObjectFactory KeyedPoolableObjectFactory},
+         and
+         {@link org.apache.commons.pool.KeyedObjectPoolFactory KeyedObjectPoolFactory}.
+      </p>
+   </body>
+</html>
diff --git a/src/site/resources/images/pool-logo-blue.jpg b/src/site/resources/images/pool-logo-blue.jpg
new file mode 100644
index 0000000..d7354c5
Binary files /dev/null and b/src/site/resources/images/pool-logo-blue.jpg differ
diff --git a/src/site/resources/images/pool-logo-white.png b/src/site/resources/images/pool-logo-white.png
new file mode 100644
index 0000000..7fc431b
Binary files /dev/null and b/src/site/resources/images/pool-logo-white.png differ
diff --git a/src/site/resources/images/pool-logo-white.xcf b/src/site/resources/images/pool-logo-white.xcf
new file mode 100644
index 0000000..8a2a4e8
Binary files /dev/null and b/src/site/resources/images/pool-logo-white.xcf differ
diff --git a/src/site/resources/images/uml/GenericKeyedObjectPool.gif b/src/site/resources/images/uml/GenericKeyedObjectPool.gif
new file mode 100644
index 0000000..11f3660
Binary files /dev/null and b/src/site/resources/images/uml/GenericKeyedObjectPool.gif differ
diff --git a/src/site/resources/images/uml/GenericObjectPool.gif b/src/site/resources/images/uml/GenericObjectPool.gif
new file mode 100644
index 0000000..0706a49
Binary files /dev/null and b/src/site/resources/images/uml/GenericObjectPool.gif differ
diff --git a/src/site/resources/images/uml/KeyedObjectPool.gif b/src/site/resources/images/uml/KeyedObjectPool.gif
new file mode 100644
index 0000000..f19155e
Binary files /dev/null and b/src/site/resources/images/uml/KeyedObjectPool.gif differ
diff --git a/src/site/resources/images/uml/ObjectPool.gif b/src/site/resources/images/uml/ObjectPool.gif
new file mode 100644
index 0000000..d56a95a
Binary files /dev/null and b/src/site/resources/images/uml/ObjectPool.gif differ
diff --git a/src/site/resources/images/uml/borrowObject.gif b/src/site/resources/images/uml/borrowObject.gif
new file mode 100644
index 0000000..71ad871
Binary files /dev/null and b/src/site/resources/images/uml/borrowObject.gif differ
diff --git a/src/site/resources/images/uml/invalidateObject.gif b/src/site/resources/images/uml/invalidateObject.gif
new file mode 100644
index 0000000..5874f33
Binary files /dev/null and b/src/site/resources/images/uml/invalidateObject.gif differ
diff --git a/src/site/resources/images/uml/returnObject.gif b/src/site/resources/images/uml/returnObject.gif
new file mode 100644
index 0000000..43649f4
Binary files /dev/null and b/src/site/resources/images/uml/returnObject.gif differ
diff --git a/src/site/site.xml b/src/site/site.xml
new file mode 100644
index 0000000..32079e9
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project name="Pool">
+    <bannerRight>
+        <name>Commons Pool</name>
+        <src>/images/pool-logo-white.png</src>
+        <href>/index.html</href>
+    </bannerRight>
+
+    <body>
+        <menu name="Pool">
+            <item name="Overview"                      href="/index.html"/>
+            <item name="Javadoc (1.2 release)"         href="http://commons.apache.org/pool/api-1.2/index.html"/>
+            <item name="Javadoc (1.3 release)"         href="http://commons.apache.org/pool/api-1.3/index.html"/>
+            <item name="Javadoc (1.4 release)"         href="http://commons.apache.org/pool/api-1.4/index.html"/>
+            <item name="Javadoc (1.5.7 release)"       href="http://commons.apache.org/pool/api-1.5.7/index.html"/>
+            <item name="Javadoc (1.6 release)"       href="http://commons.apache.org/pool/api-1.6/index.html"/>
+            <item name="Developers&#xA0;Guide"         href="/guide/index.html" collapse="true">
+                <item name="Class&#xA0;Diagrams"       href="/guide/classdiagrams.html"/>
+                <item name="Sequence&#xA0;Diagrams"    href="/guide/sequencediagrams.html"/>
+            </item>
+            <item name="Examples"                      href="/examples.html"/>
+            <item name="Downloads"                     href="/download_pool.cgi"/>
+            <item name="Wiki"                          href="http://wiki.apache.org/commons/Pool"/>
+        </menu>
+
+        <menu name="Development">
+            <item name="Mailing Lists"                 href="/mail-lists.html"/>
+            <item name="Issue Tracking"                href="/issue-tracking.html"/>
+            <item name="Source Repository"             href="/source-repository.html"/>
+            <item name="Javadoc (1.6-SNAPSHOT)"      href="apidocs/index.html"/>
+        </menu>
+    </body>
+
+</project>
diff --git a/src/site/xdoc/download_pool.xml b/src/site/xdoc/download_pool.xml
new file mode 100644
index 0000000..c8d7454
--- /dev/null
+++ b/src/site/xdoc/download_pool.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: download-page-template.xml                            |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:download-page                      |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.componentid (required, alphabetic, lower case)          |
+ |    - commons.release.version (required)                              |
+ |    - commons.binary.suffix (optional)                                |
+ |      (defaults to "-bin", set to "" for pre-maven2 releases)         |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.componentid>math</commons.componentid>                   |
+ |    <commons.release.version>1.2</commons.release.version>            |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Download Commons Pool</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+    <section name="Download Commons Pool">
+    <subsection name="Using a Mirror">
+      <p>
+        We recommend you use a mirror to download our release
+        builds, but you <strong>must</strong> verify the integrity of
+        the downloaded files using signatures downloaded from our main 
+        distribution directories. Recent releases (48 hours) may not yet
+        be available from the mirrors.
+      </p>
+
+      <p>
+        You are currently using <b>[preferred]</b>.  If you
+        encounter a problem with this mirror, please select another
+        mirror.  If all mirrors are failing, there are <i>backup</i>
+        mirrors (at the end of the mirrors list) that should be
+        available.
+        <br></br>
+        [if-any logo]<a href="[link]"><img align="right" src="[logo]" border="0"></img></a>[end]
+      </p>
+
+      <form action="[location]" method="get" id="SelectMirror">
+        <p>
+          Other mirrors: 
+          <select name="Preferred">
+          [if-any http]
+            [for http]<option value="[http]">[http]</option>[end]
+          [end]
+          [if-any ftp]
+            [for ftp]<option value="[ftp]">[ftp]</option>[end]
+          [end]
+          [if-any backup]
+            [for backup]<option value="[backup]">[backup] (backup)</option>[end]
+          [end]
+          </select>
+          <input type="submit" value="Change"></input>
+        </p>
+      </form>
+
+      <p>
+        The <a href="http://www.apache.org/dist/commons/KEYS">KEYS</a>
+        link links to the code signing keys used to sign the product.
+        The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site. 
+        The <code>MD5</code> link downloads the checksum from the main site.
+      </p>
+    </subsection>
+    </section>
+    <section name="Commons Pool 1.6 ">
+      <subsection name="Binaries">
+        <table>
+          <tr>
+              <td><a href="[preferred]/commons/pool/binaries/commons-pool-1.6-bin.tar.gz">commons-pool-1.6-bin.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/binaries/commons-pool-1.6-bin.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/binaries/commons-pool-1.6-bin.tar.gz.asc">pgp</a></td>
+          </tr>
+          <tr>
+              <td><a href="[preferred]/commons/pool/binaries/commons-pool-1.6-bin.zip">commons-pool-1.6-bin.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/binaries/commons-pool-1.6-bin.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/binaries/commons-pool-1.6-bin.zip.asc">pgp</a></td>
+          </tr>
+        </table>
+      </subsection>
+      <subsection name="Source">
+        <table>
+          <tr>
+              <td><a href="[preferred]/commons/pool/source/commons-pool-1.6-src.tar.gz">commons-pool-1.6-src.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/source/commons-pool-1.6-src.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/source/commons-pool-1.6-src.tar.gz.asc">pgp</a></td>
+          </tr>
+          <tr>
+              <td><a href="[preferred]/commons/pool/source/commons-pool-1.6-src.zip">commons-pool-1.6-src.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/source/commons-pool-1.6-src.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/pool/source/commons-pool-1.6-src.zip.asc">pgp</a></td>
+          </tr>
+        </table>
+      </subsection>
+    </section>
+    <section name="Archives">
+        <p>
+          Older releases can be obtained from the archives.
+        </p>
+        <ul>
+          <li class="download"><a href="[preferred]/commons/pool/">browse download area</a></li>
+          <li><a href="http://archive.apache.org/dist/commons/pool/">archives...</a></li>
+        </ul>
+    </section>
+  </body>
+</document>
diff --git a/src/site/xdoc/downloads.xml b/src/site/xdoc/downloads.xml
new file mode 100644
index 0000000..6f684c8
--- /dev/null
+++ b/src/site/xdoc/downloads.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<document>
+   <properties>
+      <title>Downloads</title>
+      <author email="dev at commons.apache.org">Commons Documentation Team</author>
+      <revision>$Id: downloads.xml 1222677 2011-12-23 13:41:58Z ggregory $</revision>
+   </properties>
+
+   <body>
+     <section name="Releases">
+         <p>
+            The latest release binary and source releases are always available on the
+            <a href="http://commons.apache.org/pool/download_pool.cgi">
+            Apache Commons Pool Downloads page</a>.
+         </p>
+         <ul>
+           <li>
+              Versions 1.6 adds generics based on the latest 1.5.x release, 1.5.7 at press time. It requires Java 1.5.
+           </li>
+           <li>
+              Versions 1.3 through 1.5 depend at runtime only on a Java 1.3 or better JVM.
+           </li>
+           <li>
+              Version 1.2 depends at runtime on
+            <a href="http://commons.apache.org/collections/">commons-collections</a>.
+           </li>
+         </ul>
+         <p>
+         </p>
+         <p>
+            Older releases are retained by the Apache Software Foundation but are 
+            moved into a
+            <a href="http://archive.apache.org/dist/commons/pool/">
+            special archive area</a>.
+         </p>
+         <p>
+            Access to the source tree to see the latest and greatest code is possible
+            through <a href="source-repository.html">anonymous SVN access</a>.
+         </p>
+      </section>
+
+   </body>
+</document>
+
diff --git a/src/site/xdoc/examples.xml b/src/site/xdoc/examples.xml
new file mode 100644
index 0000000..4fbb6ae
--- /dev/null
+++ b/src/site/xdoc/examples.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<document>
+   <properties>
+      <title>Examples</title>
+      <author email="dev at commons.apache.org">Commons Documentation Team</author>
+      <author email="rwaldhoff at apache.org">Rodney Waldhoff</author>
+      <revision>$Id: examples.xml 1221705 2011-12-21 13:03:54Z ggregory $</revision>
+   </properties>
+
+   <body>
+      <section name="A Simple Pool Client">
+       <p>
+        Suppose you're writing a set of <code>java.io.Reader</code> utilities, and would like to
+        provide a method for dumping the contents of a <code>Reader</code> to a <code>String</code>.
+        Here's the code for the <code>ReaderUtil</code>, implemented without an <code>ObjectPool</code>:
+       </p>
+<source>
+import java.io.Reader; 
+import java.io.IOException; 
+ 
+public class ReaderUtil { 
+    public ReaderUtil() { 
+    } 
+ 
+    /** 
+     * Dumps the contents of the {@link Reader} to a 
+     * String, closing the {@link Reader} when done. 
+     */ 
+    public String readToString(Reader in) throws IOException { 
+        StringBuffer buf = new StringBuffer(); 
+        try { 
+            for(int c = in.read(); c != -1; c = in.read()) { 
+                buf.append((char)c); 
+            } 
+            return buf.toString(); 
+        } catch(IOException e) { 
+            throw e; 
+        } finally { 
+            try { 
+                in.close(); 
+            } catch(Exception e) { 
+                // ignored 
+            } 
+        } 
+    } 
+}
+</source>
+       <p>
+        For the sake of this example, let's assume we want to to pool the <code>StringBuffer</code>s 
+        used to buffer the <code>Reader</code>'s contents. (A pool of <code>StringBuffer</code>s 
+        may or may not be useful in practice. We're just using it as a simple example here.)
+       </p>
+       <p>
+        Let's further assume that a complete pool implementation will be provided via 
+        a constructor. (We'll show you how to create such an implementation in just a moment.)  
+        Then to use the pool we simply call <code>borrowObject</code> to obtain the buffer, and
+        then call <code>returnObject</code> when we're done with it.
+        Then a <code>ReaderUtil</code> implementation using a pool of <code>StringBuffer</code>s might look 
+        like this:
+       </p>
+<source>
+import org.apache.commons.pool.ObjectPool;
+import java.io.Reader; 
+import java.io.IOException; 
+ 
+public class ReaderUtil { 
+    private ObjectPool<StringBuffer> pool;
+ 
+    public ReaderUtil(ObjectPool<StringBuffer> pool) { 
+        this.pool = pool;
+    } 
+ 
+    /** 
+     * Dumps the contents of the {@link Reader} to a 
+     * String, closing the {@link Reader} when done. 
+     */ 
+    public String readToString(Reader in) throws IOException { 
+        StringBuffer buf = null;
+        try { 
+            buf = pool.borrowObject();
+            for(int c = in.read(); c != -1; c = in.read()) { 
+                buf.append((char)c); 
+            } 
+            return buf.toString(); 
+        } catch(IOException e) { 
+            throw e; 
+        } catch(Exception e) {
+            throw new RuntimeException("Unable to borrow buffer from pool" + 
+                    e.toString());
+        } finally { 
+            try { 
+                in.close(); 
+            } catch(Exception e) { 
+                // ignored 
+            } 
+            try {
+                if(null != buf) {
+                    pool.returnObject(buf);
+                }
+            } catch(Exception e) {
+                // ignored
+            }
+        } 
+    } 
+}
+</source>
+      <p>
+       Since we've constrained ourselves to the <code>ObjectPool</code> interface, an arbitrary pool 
+       implementation (returning, in our case, <code>StringBuffer</code>s) can be used.  When a different
+       or "better" pool implemenatation comes along, we can simply drop it into our <code>ReaderUtil</code>
+       without changing a line of code.  
+      </p>
+      </section>
+      <section name="A PoolableObjectFactory">
+       <p>
+        Recall that Pool provides a simple toolkit for creating object pools.  The 
+        <code>PoolableObjectFactory</code> interface is an important part of this toolkit.
+        <code>PoolableObjectFactory</code> defines lifecycle methods for pooled objects. 
+        We can use it to separate the kinds of objects that are pooled and how they are 
+        created, persisted, or destroyed, from the pooling algorithm itself.
+       </p>
+       <p>
+        Suppose we have an <code>ObjectPool</code> implementation that accepts a 
+        <code>PoolableObjectFactory</code> (for example, any of the implementations in the
+        <code>org.apache.commons.pool.impl</code> package).  Then we need only provide 
+        the factory implemenation in order to pool a new kind of object.  
+       </p>
+       <p>
+        Here's a <code>PoolableObjectFactory</code> implementation that creates
+        <code>StringBuffer</code>s as used above.
+       </p>
+<source>
+import org.apache.commons.pool.BasePoolableObjectFactory; 
+ 
+public class StringBufferFactory extends BasePoolableObjectFactory<StringBuffer> { 
+    // for makeObject we'll simply return a new buffer 
+    public StringBuffer makeObject() { 
+        return new StringBuffer(); 
+    } 
+     
+    // when an object is returned to the pool,  
+    // we'll clear it out 
+    public void passivateObject(StringBuffer buf) { 
+        buf.setLength(0); 
+    } 
+     
+    // for all other methods, the no-op  
+    // implementation in BasePoolableObjectFactory 
+    // will suffice 
+}
+</source>
+      <p>
+       We can, for example, use this factory with the <code>StackObjectPool</code> to instantiate our
+       <code>ReaderUtil</code> as follows:
+      </p>
+<source>new ReaderUtil(new StackObjectPool<StringBuffer>(new StringBufferFactory()))</source>
+      </section>
+   </body>
+</document>
diff --git a/src/site/xdoc/guide/classdiagrams.xml b/src/site/xdoc/guide/classdiagrams.xml
new file mode 100644
index 0000000..8195dde
--- /dev/null
+++ b/src/site/xdoc/guide/classdiagrams.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<document>
+
+ <properties>
+  <title>Class Diagrams</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body>
+
+<section name="GenericObjectPool">
+<center><img src="../images/uml/GenericObjectPool.gif"/></center>
+</section>
+
+<section name="GenericKeyedObjectPool">
+<center><img src="../images/uml/GenericKeyedObjectPool.gif"/></center>
+</section>
+
+
+</body>
+</document>
diff --git a/src/site/xdoc/guide/index.xml b/src/site/xdoc/guide/index.xml
new file mode 100644
index 0000000..d7c5c2e
--- /dev/null
+++ b/src/site/xdoc/guide/index.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<document>
+
+ <properties>
+  <title>Developers Guide</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body>
+
+<section name="ObjectPool">
+<center><img src="../images/uml/ObjectPool.gif"/></center>
+<br/>
+<p>
+<a href="../apidocs/org/apache/commons/pool/ObjectPool.html">ObjectPool</a> defines a simple pooling interface.
+<ul>
+   <li>
+       <a href="../apidocs/org/apache/commons/pool/impl/GenericObjectPool.html">GenericObjectPool</a>:
+       ObjectPool implementation with configurable LIFO/FIFO behavior.
+       The default behavior is for the pool to act as a LIFO queue. What this
+       means is that when there are idle objects available in the pool, 
+       borrowObject returns the most recently returned ("last in") instance.
+       If the <code>lifo</code> the property of the pool false, instances are
+       returned in the oppposite order - first-in, first-out.
+   </li>
+   <li>
+       <a href="../apidocs/org/apache/commons/pool/impl/StackObjectPool.html">StackObjectPool</a>:
+       ObjectPool implementation with a LIFO (Last In First Out) behavior.
+   </li>
+   <li>
+       <a href="../apidocs/org/apache/commons/pool/impl/SoftReferenceObjectPool.html">SoftReferenceObjectPool</a>: 
+       ObjectPool implementation with a LIFO (Last In First Out) behavior. Additionally this pool wraps 
+       each object in a SoftReference allowing the garbage collector to remove them in response to 
+       memory demand.
+   </li>
+</ul>
+</p>
+</section>
+
+<section name="KeyedObjectPool">
+<center><img src="../images/uml/KeyedObjectPool.gif"/></center>
+<br/>
+<p>
+A <a href="../apidocs/org/apache/commons/pool/KeyedObjectPool.html">KeyedObjectPool</a>
+pools instances of multiple types. Each type may be accessed using an arbitrary key.
+<ul>
+   <li>
+       <a href="../apidocs/org/apache/commons/pool/impl/GenericKeyedObjectPool.html">GenericKeyedObjectPool</a>:
+       ObjectPool implementation with a FIFO (First In First Out) behavior.
+   </li>
+   <li>
+       <a href="../apidocs/org/apache/commons/pool/impl/StackKeyedObjectPool.html">StackKeyedObjectPool</a>:
+       ObjectPool implementation with a LIFO (Last In First Out) behavior.
+   </li>
+</ul>
+</p>
+</section>
+
+</body>
+</document>
diff --git a/src/site/xdoc/guide/sequencediagrams.xml b/src/site/xdoc/guide/sequencediagrams.xml
new file mode 100644
index 0000000..a649224
--- /dev/null
+++ b/src/site/xdoc/guide/sequencediagrams.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<document>
+
+ <properties>
+  <title>Sequence Diagrams</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body>
+
+<section name="borrowObject">
+<center><img src="../images/uml/borrowObject.gif"/></center>
+</section>
+
+<section name="returnObject">
+<center><img src="../images/uml/returnObject.gif"/></center>
+</section>
+
+<section name="invalidateObject">
+<center><img src="../images/uml/invalidateObject.gif"/></center>
+</section>
+
+</body>
+</document>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
new file mode 100644
index 0000000..bfaa755
--- /dev/null
+++ b/src/site/xdoc/index.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<document>
+   <properties>
+      <title>Overview</title>
+      <author email="dev at commons.apache.org">Commons Documentation Team</author>
+      <author email="rwaldhoff at apache.org">Rodney Waldhoff</author>
+      <revision>$Id: index.xml 1221705 2011-12-21 13:03:54Z ggregory $</revision>
+   </properties>
+
+   <body>
+      <section name="The Pool Component">
+         <p>
+          Pool provides an Object-pooling API, with three major aspects:
+          <ol>
+           <li>
+            A generic object pool interface that clients and implementors can use to provide easily 
+            interchangable pooling implementations.
+           </li>
+           <li>
+            A toolkit for creating modular object pools.
+           </li>
+           <li>
+            Several general purpose pool implementations.
+           </li>
+          </ol>
+         </p>
+      </section>
+
+      <section name="Releases">
+         <p>
+            See the <a href="downloads.html">downloads</a> page for information on obtaining releases.
+         </p>
+      </section>
+
+      <section name="Features">
+        <p>
+            The 
+            <a href="./apidocs/org/apache/commons/pool/package-summary.html">org.apache.commons.pool</a> 
+            package defines a handful of pooling interfaces and some base classes 
+            that may be useful when creating new pool implementations.
+        </p>
+        <subsection name="ObjectPool">
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/ObjectPool.html"><code>ObjectPool</code></a>
+             defines a trivially simple pooling interface:
+          </p>
+<source>
+public interface ObjectPool<T> {
+    T borrowObject();
+    void returnObject(T borrowed);
+}
+</source>           
+          <p>
+            Some client classes won't integrate with <i>Pool</i> any more than this.
+            Clients written to this interface can use arbitrary <code>ObjectPool</code> 
+            implementations interchangeably. 
+          </p>           
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/BaseObjectPool.html"><code>BaseObjectPool</code></a>
+             provides an abstract base implementation of <code>ObjectPool</code>. Clients are
+             encouraged but not required to extend <code>BaseObjectPool</code> for new 
+             <code>ObjectPool</code> implementations.
+          </p>
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/KeyedObjectPool.html"><code>KeyedObjectPool</code></a>
+             defines a similar interface for pools composed of heterogeneous objects:
+          </p>
+<source>
+public interface KeyedObjectPool<K, V> {
+    V borrowObject(K key);
+    void returnObject(K key, V borrowed);
+}
+</source>           
+        </subsection>
+        <subsection name="PoolableObjectFactory">
+          <p>
+             The <i>Pool</i> package makes it possible separate the way in which instances
+             are pooled from the way in which instances are created and destroyed. 
+             <a href="./apidocs/org/apache/commons/pool/PoolableObjectFactory.html"><code>PoolableObjectFactory</code></a>
+             supports this by providing a generic interface for the lifecycle of a pooled object:
+          </p>
+<source>
+public interface PoolableObjectFactory<T> {
+    T makeObject();
+    void activateObject(T obj);
+    void passivateObject(T obj);
+    boolean validateObject(T obj);
+    void destroyObject(T obj);
+}
+</source>           
+          <p>
+             <code>ObjectPool</code> implementations may be written to accept arbitrary
+             <code>PoolableObjectFactory</code>s.
+             This makes is possible for clients to select pooling-behavior distinct 
+             from the kinds of objects that are pooled.  
+          </p>           
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/BasePoolableObjectFactory.html"><code>BasePoolableObjectFactory</code></a>
+             provides an abstract base implementation of <code>PoolableObjectFactory</code> that
+             makes implementations a snap.
+          </p>
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/KeyedPoolableObjectFactory.html"><code>KeyedPoolableObjectFactory</code></a>
+             defines a similar interface for <code>KeyedObjectPool</code>s:
+          </p>
+<source>
+public interface KeyedPoolableObjectFactory<K, V> {
+    V makeObject(K key);
+    void activateObject(K key, V obj);
+    void passivateObject(K key, V obj);
+    boolean validateObject(K key, V obj);
+    void destroyObject(K key, V obj);
+}
+</source>           
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/BaseKeyedPoolableObjectFactory.html"><code>BaseKeyedPoolableObjectFactory</code></a>
+             provides an abstract base implementation of <code>KeyedPoolableObjectFactory</code> that
+             makes implementations a snap.
+          </p>
+        </subsection>
+        <p>
+            The 
+            <a href="./apidocs/org/apache/commons/pool/impl/package-summary.html">org.apache.commons.pool.impl</a> 
+            package provides some <i>Pool</i> implementations.
+        </p>
+        <subsection name="StackObjectPool">
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/impl/StackObjectPool.html"><code>StackObjectPool</code></a>
+             will pool a finite number of "idle" instances, but will create new instances a needed in 
+             order to support high demand.
+          </p>
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/impl/StackKeyedObjectPool.html"><code>StackKeyedObjectPool</code></a>
+             offers the same behavior for keyed pools.
+          </p>
+        </subsection>
+        <subsection name="GenericObjectPool">
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/impl/GenericObjectPool.html"><code>GenericObjectPool</code></a>
+             provides a wide variety of configuration options, including the ability to cap the number of idle or
+             active instances, to evict instances as they sit idle in the pool, etc.
+          </p>
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/impl/GenericKeyedObjectPool.html"><code>GenericKeyedObjectPool</code></a>
+             offers the same behavior for keyed pools.
+          </p>
+        </subsection>
+        <subsection name="SoftReferenceObjectPool">
+          <p>
+             <a href="./apidocs/org/apache/commons/pool/impl/SoftReferenceObjectPool.html"><code>SoftReferenceObjectPool</code></a>
+             can grow as needed, but allows the garbage collector to evict idle instances from the pool as needed. 
+          </p>
+        </subsection>
+      </section>
+   </body>
+</document>
diff --git a/src/site/xdoc/issue-tracking.xml b/src/site/xdoc/issue-tracking.xml
new file mode 100644
index 0000000..0642382
--- /dev/null
+++ b/src/site/xdoc/issue-tracking.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: issue-tracking-template.xml                           |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:jira-page                          |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.jira.id  (required, alphabetic, upper case)             |
+ |    - commons.jira.pid (required, numeric)                            |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.jira.id>MATH</commons.jira.id>                           |
+ |    <commons.jira.pid>12310485</commons.jira.pid>                     |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Commons Pool Issue tracking</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+
+    <section name="Commons Pool Issue tracking">
+      <p>
+      Commons Pool uses <a href="http://issues.apache.org/jira/">ASF JIRA</a> for tracking issues.
+      See the <a href="http://issues.apache.org/jira/browse/POOL">Commons Pool JIRA project page</a>.
+      </p>
+
+      <p>
+      To use JIRA you may need to <a href="http://issues.apache.org/jira/secure/Signup!default.jspa">create an account</a>
+      (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically
+      created and you can use the <a href="http://issues.apache.org/jira/secure/ForgotPassword!default.jspa">Forgot Password</a>
+      page to get a new password).
+      </p>
+
+      <p>
+      If you would like to report a bug, or raise an enhancement request with
+      Commons Pool please do the following:
+      <ol>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310488&sorter/field=issuekey&sorter/order=DESC&status=1&status=3&status=4">Search existing open bugs</a>.
+            If you find your issue listed then please add a comment with your details.</li>
+        <li><a href="mail-lists.html">Search the mailing list archive(s)</a>.
+            You may find your issue or idea has already been discussed.</li>
+        <li>Decide if your issue is a bug or an enhancement.</li>
+        <li>Submit either a <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310488&issuetype=1&priority=4&assignee=-1">bug report</a>
+            or <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310488&issuetype=4&priority=4&assignee=-1">enhancement request</a>.</li>
+      </ol>
+      </p>
+
+      <p>
+      Please also remember these points:
+      <ul>
+        <li>the more information you provide, the better we can help you</li>
+        <li>test cases are vital, particularly for any proposed enhancements</li>
+        <li>the developers of Commons Pool are all unpaid volunteers</li>
+      </ul>
+      </p>
+
+      <p>
+      For more information on subversion and creating patches see the
+      <a href="http://www.apache.org/dev/contributors.html">Apache Contributors Guide</a>.
+      </p>
+
+      <p>
+      You may also find these links useful:
+      <ul>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310488&sorter/field=issuekey&sorter/order=DESC&status=1&status=3&status=4">All Open Commons Pool bugs</a></li>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310488&sorter/field=issuekey&sorter/order=DESC&status=5&status=6">All Resolved Commons Pool bugs</a></li>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310488&sorter/field=issuekey&sorter/order=DESC">All Commons Pool bugs</a></li>
+      </ul>
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/src/site/xdoc/mail-lists.xml b/src/site/xdoc/mail-lists.xml
new file mode 100644
index 0000000..d4f6bc2
--- /dev/null
+++ b/src/site/xdoc/mail-lists.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: mail-lists-template.xml                               |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:mail-page                          |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.componentid (required, alphabetic, lower case)          |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.componentid>math</commons.componentid>                   |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Commons Pool Mailing Lists</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+
+    <section name="Overview">
+      <p>
+        <a href="index.html">Commons Pool</a> shares mailing lists with all the other 
+        <a href="http://commons.apache.org/components.html">Commons Components</a>.
+        To make it easier for people to only read messages related to components they are interested in,
+        the convention in Commons is to prefix the subject line of messages with the component's name,
+        for example:
+        <ul>
+          <li>[pool] Problem with the ...</li>
+        </ul>
+      </p>
+      <p>
+        Questions related to the usage of Commons Pool should be posted to the
+        <a href="http://mail-archives.apache.org/mod_mbox/commons-user/">User List</a>.
+        <br />
+        The <a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">Developer List</a>
+        is for questions and discussion related to the development of Commons Pool.
+        <br />
+        Please do not cross-post; developers are also subscribed to the user list.
+      </p>
+      <p>
+        <strong>Note:</strong> please don't send patches or attachments to any of the mailing lists.
+        Patches are best handled via the <a href="issue-tracking.html">Issue Tracking</a> system. 
+        Otherwise, please upload the file to a public server and include the URL in the mail. 
+      </p>
+    </section>
+
+    <section name="Commons Pool Mailing Lists">
+      <p>
+        <strong>Please prefix the subject line of any messages for <a href="index.html">Commons Pool</a>
+        with <i>[pool]</i></strong> - <i>thanks!</i>
+        <br />
+        <br />
+      </p>
+
+      <table>
+        <tr>
+          <th>Name</th>
+          <th>Subscribe</th>
+          <th>Unsubscribe</th>
+          <th>Post</th>
+          <th>Archive</th>
+          <th>Other Archives</th>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons User List</strong>
+            <br /><br />
+            Questions on using Commons Pool.
+            <br /><br />
+          </td>
+          <td><a href="mailto:user-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:user-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><a href="mailto:user at commons.apache.org?subject=[pool]">Post</a></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-user/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.users/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/user@commons.apache.org/">www.mail-archive.com</a><br />
+              <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
+          </td>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons Developer List</strong>
+            <br /><br />
+            Discussion of development of Commons Pool.
+            <br /><br />
+          </td>
+          <td><a href="mailto:dev-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:dev-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><a href="mailto:dev at commons.apache.org?subject=[pool]">Post</a></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.dev/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/dev@commons.apache.org/">www.mail-archive.com</a><br />
+              <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
+          </td>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons Issues List</strong>
+            <br /><br />
+            Only for e-mails automatically generated by the <a href="issue-tracking.html">issue tracking</a> system.
+            <br /><br />
+          </td>
+          <td><a href="mailto:issues-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:issues-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><i>read only</i></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-issues/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.issues/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/issues@commons.apache.org/">www.mail-archive.com</a>
+          </td>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons Commits List</strong>
+            <br /><br />
+            Only for e-mails automatically generated by the <a href="source-repository.html">source control</a> sytem.
+            <br /><br />
+          </td>
+          <td><a href="mailto:commits-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:commits-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><i>read only</i></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-commits/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.commits/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/commits@commons.apache.org/">www.mail-archive.com</a>
+          </td>
+        </tr>
+
+      </table>
+
+    </section>
+    <section name="Apache Mailing Lists">
+      <p>
+        Other mailing lists which you may find useful include:
+      </p>
+
+      <table>
+        <tr>
+          <th>Name</th>
+          <th>Subscribe</th>
+          <th>Unsubscribe</th>
+          <th>Post</th>
+          <th>Archive</th>
+          <th>Other Archives</th>
+        </tr>
+        <tr>
+          <td>
+            <strong>Apache Announce List</strong>
+            <br /><br />
+            General announcements of Apache project releases.
+            <br /><br />
+          </td>
+          <td><a class="externalLink" href="mailto:announce-subscribe at apache.org">Subscribe</a></td> 
+          <td><a class="externalLink" href="mailto:announce-unsubscribe at apache.org">Unsubscribe</a></td> 
+          <td><i>read only</i></td>
+          <td><a class="externalLink" href="http://mail-archives.apache.org/mod_mbox/announce/">mail-archives.apache.org</a></td> 
+          <td><a class="externalLink" href="http://markmail.org/list/org.apache.announce/">markmail.org</a><br />
+              <a class="externalLink" href="http://old.nabble.com/Apache-News-and-Announce-f109.html">old.nabble.com</a><br />
+              <a class="externalLink" href="http://www.mail-archive.com/announce@apache.org/">www.mail-archive.com</a><br />
+              <a class="externalLink" href="http://news.gmane.org/gmane.comp.apache.announce">news.gmane.org</a>
+          </td>
+        </tr>
+      </table>
+
+    </section>
+  </body>
+</document>
diff --git a/src/template/pool-release-notes.vm b/src/template/pool-release-notes.vm
new file mode 100644
index 0000000..bd00899
--- /dev/null
+++ b/src/template/pool-release-notes.vm
@@ -0,0 +1,119 @@
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##  http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied.  See the License for the
+## specific language governing permissions and limitations
+## under the License.
+                        
+
+              ${finalName} RELEASE NOTES
+
+${release.description}
+
+#if ($release.getActions().size() == 0)
+No changes defined in this version.
+#else
+Changes in this version include:
+
+#if ($release.getActions('add').size() !=0)
+New features:
+#foreach($actionItem in $release.getActions('add'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o #if($!issue != "") $issue: #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end 
+#end
+
+#if ($release.getActions('fix').size() !=0)
+Fixed Bugs:
+#foreach($actionItem in $release.getActions('fix'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o #if($!issue != "") $issue: #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+
+#if ($release.getActions('update').size() !=0)
+Changes:
+#foreach($actionItem in $release.getActions('update'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o #if($!issue != "") $issue: #end ${action} #if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+
+#if ($release.getActions('remove').size() !=0)
+Removed:
+#foreach($actionItem in $release.getActions('remove'))
+#set($action=$actionItem.getAction())
+#if ($actionItem.getIssue())
+#set($issue=$actionItem.getIssue())
+#else
+#set($issue="")
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=$actionItem.getDueTo())
+#else
+#set($dueto="")
+#end
+o ${action} #if($!issue != "") Issue: $issue. #end#if($!dueto != "")Thanks to $dueto. #end
+
+#set($issue="")
+#set($dueto="")
+#end
+#end
+## End of main loop
+#end
+ 
+For complete information on commons-pool, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the commons-pool website:
+
+http://commons.apache.org/pool/
+
+
diff --git a/src/test/org/apache/commons/pool/MethodCall.java b/src/test/org/apache/commons/pool/MethodCall.java
new file mode 100644
index 0000000..3c441cb
--- /dev/null
+++ b/src/test/org/apache/commons/pool/MethodCall.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Holds method names, parameters, and return values for tracing method calls.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
+ */
+public class MethodCall {
+    private final String name;
+    private final List<?> params;
+    private Object returned;
+
+    public MethodCall(final String name) {
+        this(name, null);
+    }
+
+    public MethodCall(final String name, final Object param) {
+        this(name, Collections.singletonList(param));
+    }
+
+    public MethodCall(final String name, final Object param1, final Object param2) {
+        this(name, Arrays.asList(new Object[] {param1, param2}));
+    }
+
+    public MethodCall(final String name, final List<?> params) {
+        if (name == null) {
+            throw new IllegalArgumentException("name must not be null.");
+        }
+        this.name = name;
+        if (params != null) {
+            this.params = params;
+        } else {
+            this.params = Collections.EMPTY_LIST;
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List<?> getParams() {
+        return params;
+    }
+
+    public Object getReturned() {
+        return returned;
+    }
+
+    public void setReturned(final Object returned) {
+        this.returned = returned;
+    }
+
+    public MethodCall returned(Object obj) {
+        setReturned(obj);
+        return this;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final MethodCall that = (MethodCall)o;
+
+        if (name != null ? !name.equals(that.name) : that.name != null) return false;
+        if (params != null ? !params.equals(that.params) : that.params != null) return false;
+        if (returned != null ? !returned.equals(that.returned) : that.returned != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result;
+        result = (name != null ? name.hashCode() : 0);
+        result = 29 * result + (params != null ? params.hashCode() : 0);
+        result = 29 * result + (returned != null ? returned.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer();
+        sb.append("MethodCall");
+        sb.append("{name='").append(name).append('\'');
+        if (!params.isEmpty()) {
+            sb.append(", params=").append(params);
+        }
+        if (returned != null) {
+            sb.append(", returned=").append(returned);
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/src/test/org/apache/commons/pool/MethodCallPoolableObjectFactory.java b/src/test/org/apache/commons/pool/MethodCallPoolableObjectFactory.java
new file mode 100644
index 0000000..a5e6a00
--- /dev/null
+++ b/src/test/org/apache/commons/pool/MethodCallPoolableObjectFactory.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A poolable object factory that tracks how {@link MethodCall methods are called}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
+ * @see MethodCall
+ */
+public class MethodCallPoolableObjectFactory implements PoolableObjectFactory<Integer> {
+    private final List<MethodCall> methodCalls = new ArrayList<MethodCall>();
+    private int count = 0;
+    private boolean valid = true;
+    private boolean makeObjectFail;
+    private boolean activateObjectFail;
+    private boolean validateObjectFail;
+    private boolean passivateObjectFail;
+    private boolean destroyObjectFail;
+
+    public void reset() {
+        count = 0;
+        getMethodCalls().clear();
+        setMakeObjectFail(false);
+        setActivateObjectFail(false);
+        setValid(true);
+        setValidateObjectFail(false);
+        setPassivateObjectFail(false);
+        setDestroyObjectFail(false);
+    }
+
+    public List<MethodCall> getMethodCalls() {
+        return methodCalls;
+    }
+
+    public int getCurrentCount() {
+        return count;
+    }
+
+    public void setCurrentCount(final int count) {
+        this.count = count;
+    }
+
+    public boolean isMakeObjectFail() {
+        return makeObjectFail;
+    }
+
+    public void setMakeObjectFail(final boolean makeObjectFail) {
+        this.makeObjectFail = makeObjectFail;
+    }
+
+    public boolean isDestroyObjectFail() {
+        return destroyObjectFail;
+    }
+
+    public void setDestroyObjectFail(final boolean destroyObjectFail) {
+        this.destroyObjectFail = destroyObjectFail;
+    }
+
+    public boolean isValid() {
+        return valid;
+    }
+
+    public void setValid(final boolean valid) {
+        this.valid = valid;
+    }
+
+    public boolean isValidateObjectFail() {
+        return validateObjectFail;
+    }
+
+    public void setValidateObjectFail(final boolean validateObjectFail) {
+        this.validateObjectFail = validateObjectFail;
+    }
+
+    public boolean isActivateObjectFail() {
+        return activateObjectFail;
+    }
+
+    public void setActivateObjectFail(final boolean activateObjectFail) {
+        this.activateObjectFail = activateObjectFail;
+    }
+
+    public boolean isPassivateObjectFail() {
+        return passivateObjectFail;
+    }
+
+    public void setPassivateObjectFail(final boolean passivateObjectFail) {
+        this.passivateObjectFail = passivateObjectFail;
+    }
+
+    public Integer makeObject() throws Exception {
+        final MethodCall call = new MethodCall("makeObject");
+        methodCalls.add(call);
+        int count = this.count++;
+        if (makeObjectFail) {
+            throw new PrivateException("makeObject");
+        }
+        final Integer obj = new Integer(count);
+        call.setReturned(obj);
+        return obj;
+    }
+
+    public void activateObject(final Integer obj) throws Exception {
+        methodCalls.add(new MethodCall("activateObject", obj));
+        if (activateObjectFail) {
+            throw new PrivateException("activateObject");
+        }
+    }
+
+    public boolean validateObject(final Integer obj) {
+        final MethodCall call = new MethodCall("validateObject", obj);
+        methodCalls.add(call);
+        if (validateObjectFail) {
+            throw new PrivateException("validateObject");
+        }
+        final boolean r = valid;
+        call.returned(new Boolean(r));
+        return r;
+    }
+
+    public void passivateObject(final Integer obj) throws Exception {
+        methodCalls.add(new MethodCall("passivateObject", obj));
+        if (passivateObjectFail) {
+            throw new PrivateException("passivateObject");
+        }
+    }
+
+    public void destroyObject(final Integer obj) throws Exception {
+        methodCalls.add(new MethodCall("destroyObject", obj));
+        if (destroyObjectFail) {
+            throw new PrivateException("destroyObject");
+        }
+    }
+}
diff --git a/src/test/org/apache/commons/pool/PrivateException.java b/src/test/org/apache/commons/pool/PrivateException.java
new file mode 100644
index 0000000..38a0f13
--- /dev/null
+++ b/src/test/org/apache/commons/pool/PrivateException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * An exception that only is thrown by these tests.
+ */
+public class PrivateException extends RuntimeException {
+    public PrivateException(final String message) {
+        super(message);
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestBaseKeyedObjectPool.java b/src/test/org/apache/commons/pool/TestBaseKeyedObjectPool.java
new file mode 100644
index 0000000..c38fc53
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestBaseKeyedObjectPool.java
@@ -0,0 +1,367 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+
+/**
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestBaseKeyedObjectPool<K, V> extends TestKeyedObjectPool {
+    private KeyedObjectPool<K, V> _pool = null;
+
+    public TestBaseKeyedObjectPool(final String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected KeyedObjectPool<Object, Integer> makeEmptyPool(KeyedPoolableObjectFactory<Object, Integer> factory) {
+        if (this.getClass() != TestBaseKeyedObjectPool.class) {
+            fail("Subclasses of TestBaseKeyedObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseKeyedObjectPool isn't a complete implementation.");
+    }
+
+    /**
+     * Create an {@link KeyedObjectPool} instance
+     * that can contain at least <i>mincapacity</i>
+     * idle and active objects, or
+     * throw {@link IllegalArgumentException}
+     * if such a pool cannot be created.
+     */
+    protected KeyedObjectPool<K, V> makeEmptyPool(int mincapacity) {
+        if (this.getClass() != TestBaseKeyedObjectPool.class) {
+            fail("Subclasses of TestBaseKeyedObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseKeyedObjectPool isn't a complete implementation.");
+    }
+
+    /**
+     * Return what we expect to be the n<sup>th</sup>
+     * object (zero indexed) created by the pool
+     * for the given key.
+     */
+    protected V getNthObject(Object key, int n) {
+        if (this.getClass() != TestBaseKeyedObjectPool.class) {
+            fail("Subclasses of TestBaseKeyedObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseKeyedObjectPool isn't a complete implementation.");
+    }
+
+    protected K makeKey(int n) {
+        if (this.getClass() != TestBaseKeyedObjectPool.class) {
+            fail("Subclasses of TestBaseKeyedObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseKeyedObjectPool isn't a complete implementation.");
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        _pool = null;
+        super.tearDown();
+    }
+
+    public void testUnsupportedOperations() throws Exception {
+        if (!getClass().equals(TestBaseKeyedObjectPool.class)) {
+            return; // skip redundant tests
+        }
+        KeyedObjectPool<String, String> pool = new BaseKeyedObjectPool<String, String>() { 
+            @Override
+            public String borrowObject(String key) {
+                return null;
+            }
+            @Override
+            public void returnObject(String key, String obj) {
+            }
+            @Override
+            public void invalidateObject(String key, String obj) {
+            }            
+        };
+        
+        try {
+            pool.addObject("key");
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        assertTrue("Negative expected.", pool.getNumIdle() < 0);
+        assertTrue("Negative expected.", pool.getNumIdle("key") < 0);
+        assertTrue("Negative expected.", pool.getNumActive() < 0);
+        assertTrue("Negative expected.", pool.getNumActive("key") < 0);
+
+        try {
+            pool.clear();
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            pool.clear("key");
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            pool.setFactory(null);
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        pool.close(); // a no-op, probably should be remove
+
+    }
+
+    protected boolean isLifo() {
+        if (getClass() != TestBaseKeyedObjectPool.class) {
+            fail("Subclasses of TestBaseKeyedObjectPool must reimplement this method.");
+        }
+        return false;
+    }
+
+    protected boolean isFifo() {
+        if (getClass() != TestBaseKeyedObjectPool.class) {
+            fail("Subclasses of TestBaseKeyedObjectPool must reimplement this method.");
+        }
+        return false;
+    }
+
+    public void testBaseBorrowReturn() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K keya = makeKey(0);
+        V obj0 = _pool.borrowObject(keya);
+        assertEquals(getNthObject(keya,0),obj0);
+        V obj1 = _pool.borrowObject(keya);
+        assertEquals(getNthObject(keya,1),obj1);
+        V obj2 = _pool.borrowObject(keya);
+        assertEquals(getNthObject(keya,2),obj2);
+        _pool.returnObject(keya,obj2);
+        obj2 = _pool.borrowObject(keya);
+        assertEquals(getNthObject(keya,2),obj2);
+        _pool.returnObject(keya,obj1);
+        obj1 = _pool.borrowObject(keya);
+        assertEquals(getNthObject(keya,1),obj1);
+        _pool.returnObject(keya,obj0);
+        _pool.returnObject(keya,obj2);
+        obj2 = _pool.borrowObject(keya);
+        if (isLifo()) {
+            assertEquals(getNthObject(keya,2),obj2);
+        }
+        if (isFifo()) {
+            assertEquals(getNthObject(keya,0),obj2);
+        }
+        obj0 = _pool.borrowObject(keya);
+        if (isLifo()) {
+            assertEquals(getNthObject(keya,0),obj0);
+        }
+        if (isFifo()) {
+            assertEquals(getNthObject(keya,2),obj0);
+        }
+    }
+
+    public void testBaseBorrow() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K keya = makeKey(0);
+        K keyb = makeKey(1);
+        assertEquals("1",getNthObject(keya,0),_pool.borrowObject(keya));
+        assertEquals("2",getNthObject(keyb,0),_pool.borrowObject(keyb));
+        assertEquals("3",getNthObject(keyb,1),_pool.borrowObject(keyb));
+        assertEquals("4",getNthObject(keya,1),_pool.borrowObject(keya));
+        assertEquals("5",getNthObject(keyb,2),_pool.borrowObject(keyb));
+        assertEquals("6",getNthObject(keya,2),_pool.borrowObject(keya));
+    }
+
+    public void testBaseNumActiveNumIdle() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K keya = makeKey(0);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        V obj0 = _pool.borrowObject(keya);
+        assertEquals(1,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        V obj1 = _pool.borrowObject(keya);
+        assertEquals(2,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        _pool.returnObject(keya,obj1);
+        assertEquals(1,_pool.getNumActive(keya));
+        assertEquals(1,_pool.getNumIdle(keya));
+        _pool.returnObject(keya,obj0);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(2,_pool.getNumIdle(keya));
+
+        // cast to KeyedObjectPool to use the wrong key type.
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Object, V> rawPool = (KeyedObjectPool<Object, V>)_pool;
+        assertEquals(0,rawPool.getNumActive("xyzzy12345"));
+        assertEquals(0,rawPool.getNumIdle("xyzzy12345"));
+    }
+
+    public void testBaseNumActiveNumIdle2() throws Exception {
+        try {
+            _pool = makeEmptyPool(6);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K keya = makeKey(0);
+        K keyb = makeKey(1);
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        assertEquals(0,_pool.getNumActive(keyb));
+        assertEquals(0,_pool.getNumIdle(keyb));
+
+        V objA0 = _pool.borrowObject(keya);
+        V objB0 = _pool.borrowObject(keyb);
+
+        assertEquals(2,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        assertEquals(1,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        assertEquals(1,_pool.getNumActive(keyb));
+        assertEquals(0,_pool.getNumIdle(keyb));
+
+        V objA1 = _pool.borrowObject(keya);
+        V objB1 = _pool.borrowObject(keyb);
+
+        assertEquals(4,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        assertEquals(2,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        assertEquals(2,_pool.getNumActive(keyb));
+        assertEquals(0,_pool.getNumIdle(keyb));
+
+        _pool.returnObject(keya,objA0);
+        _pool.returnObject(keyb,objB0);
+
+        assertEquals(2,_pool.getNumActive());
+        assertEquals(2,_pool.getNumIdle());
+        assertEquals(1,_pool.getNumActive(keya));
+        assertEquals(1,_pool.getNumIdle(keya));
+        assertEquals(1,_pool.getNumActive(keyb));
+        assertEquals(1,_pool.getNumIdle(keyb));
+
+        _pool.returnObject(keya,objA1);
+        _pool.returnObject(keyb,objB1);
+
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(4,_pool.getNumIdle());
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(2,_pool.getNumIdle(keya));
+        assertEquals(0,_pool.getNumActive(keyb));
+        assertEquals(2,_pool.getNumIdle(keyb));
+    }
+
+    public void testBaseClear() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K keya = makeKey(0);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        V obj0 = _pool.borrowObject(keya);
+        V obj1 = _pool.borrowObject(keya);
+        assertEquals(2,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        _pool.returnObject(keya,obj1);
+        _pool.returnObject(keya,obj0);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(2,_pool.getNumIdle(keya));
+        _pool.clear(keya);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        Object obj2 = _pool.borrowObject(keya);
+        assertEquals(getNthObject(keya,2),obj2);
+    }
+
+    public void testBaseInvalidateObject() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K keya = makeKey(0);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        V obj0 = _pool.borrowObject(keya);
+        V obj1 = _pool.borrowObject(keya);
+        assertEquals(2,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        _pool.invalidateObject(keya,obj0);
+        assertEquals(1,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+        _pool.invalidateObject(keya,obj1);
+        assertEquals(0,_pool.getNumActive(keya));
+        assertEquals(0,_pool.getNumIdle(keya));
+    }
+
+    public void testBaseAddObject() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException uoe) {
+            return; // skip this test if unsupported
+        }
+        K key = makeKey(0);
+        try {
+            assertEquals(0,_pool.getNumIdle());
+            assertEquals(0,_pool.getNumActive());
+            assertEquals(0,_pool.getNumIdle(key));
+            assertEquals(0,_pool.getNumActive(key));
+            _pool.addObject(key);
+            assertEquals(1,_pool.getNumIdle());
+            assertEquals(0,_pool.getNumActive());
+            assertEquals(1,_pool.getNumIdle(key));
+            assertEquals(0,_pool.getNumActive(key));
+            V obj = _pool.borrowObject(key);
+            assertEquals(getNthObject(key,0),obj);
+            assertEquals(0,_pool.getNumIdle());
+            assertEquals(1,_pool.getNumActive());
+            assertEquals(0,_pool.getNumIdle(key));
+            assertEquals(1,_pool.getNumActive(key));
+            _pool.returnObject(key,obj);
+            assertEquals(1,_pool.getNumIdle());
+            assertEquals(0,_pool.getNumActive());
+            assertEquals(1,_pool.getNumIdle(key));
+            assertEquals(0,_pool.getNumActive(key));
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if one of those calls is unsupported
+        }
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestBaseKeyedPoolableObjectFactory.java b/src/test/org/apache/commons/pool/TestBaseKeyedPoolableObjectFactory.java
new file mode 100644
index 0000000..9932041
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestBaseKeyedPoolableObjectFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestBaseKeyedPoolableObjectFactory extends TestCase {
+    public TestBaseKeyedPoolableObjectFactory(String testName) {
+        super(testName);
+    }
+
+    public void testDefaultMethods() throws Exception {
+        KeyedPoolableObjectFactory<String, Object> factory = new BaseKeyedPoolableObjectFactory<String, Object>() { 
+            @Override
+            public Object makeObject(String key) throws Exception {
+                return null;
+            }
+        };   
+        
+        factory.activateObject("key",null); // a no-op
+        factory.passivateObject("key",null); // a no-op
+        factory.destroyObject("key",null); // a no-op
+        assertTrue(factory.validateObject("key",null)); // constant true
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestBaseObjectPool.java b/src/test/org/apache/commons/pool/TestBaseObjectPool.java
new file mode 100644
index 0000000..b1c7d65
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestBaseObjectPool.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+
+/**
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $ 
+ */
+public class TestBaseObjectPool<T> extends TestObjectPool {
+    private ObjectPool<T> _pool = null;
+
+    public TestBaseObjectPool(String testName) {
+        super(testName);
+    }
+
+    protected ObjectPool<T> makeEmptyPool(int mincapacity) {
+        if (this.getClass() != TestBaseObjectPool.class) {
+            fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
+    }
+
+    @Override
+    protected ObjectPool<Integer> makeEmptyPool(final PoolableObjectFactory<Integer> factory) {
+        if (this.getClass() != TestBaseObjectPool.class) {
+            fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
+    }
+
+    protected T getNthObject(final int n) {
+        if (this.getClass() != TestBaseObjectPool.class) {
+            fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+        }
+        throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
+    }
+
+    protected boolean isLifo() {
+        if (this.getClass() != TestBaseObjectPool.class) {
+            fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+        }
+        return false;
+    }
+
+    protected boolean isFifo() {
+        if (this.getClass() != TestBaseObjectPool.class) {
+            fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+        }
+        return false;
+    }
+
+    // tests
+    public void testUnsupportedOperations() throws Exception {
+        if (!getClass().equals(TestBaseObjectPool.class)) {
+            return; // skip redundant tests
+        }
+        ObjectPool<Object> pool = new BaseObjectPool<Object>() { 
+            @Override
+            public Object borrowObject() {
+                return null;
+            }
+            @Override
+            public void returnObject(Object obj) {
+            }
+            @Override
+            public void invalidateObject(Object obj) {
+            }            
+        };   
+
+        assertTrue("Negative expected.", pool.getNumIdle() < 0);
+        assertTrue("Negative expected.", pool.getNumActive() < 0);
+
+        try {
+            pool.clear();
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            pool.addObject();
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            pool.setFactory(null);
+            fail("Expected UnsupportedOperationException");
+        } catch(UnsupportedOperationException e) {
+            // expected
+        }
+    }
+
+    public void testClose() throws Exception {
+        ObjectPool<Object> pool = new BaseObjectPool<Object>() {
+            @Override
+            public Object borrowObject() {
+                return null;
+            }
+            @Override
+            public void returnObject(Object obj) {
+            }
+            @Override
+            public void invalidateObject(Object obj) {
+            }
+        };
+
+        pool.close();
+        pool.close(); // should not error as of Pool 2.0.
+    }
+
+    public void testBaseBorrow() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        assertEquals(getNthObject(0),_pool.borrowObject());
+        assertEquals(getNthObject(1),_pool.borrowObject());
+        assertEquals(getNthObject(2),_pool.borrowObject());
+    }
+
+    public void testBaseAddObject() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        try {
+            assertEquals(0,_pool.getNumIdle());
+            assertEquals(0,_pool.getNumActive());
+            _pool.addObject();
+            assertEquals(1,_pool.getNumIdle());
+            assertEquals(0,_pool.getNumActive());
+            T obj = _pool.borrowObject();
+            assertEquals(getNthObject(0),obj);
+            assertEquals(0,_pool.getNumIdle());
+            assertEquals(1,_pool.getNumActive());
+            _pool.returnObject(obj);
+            assertEquals(1,_pool.getNumIdle());
+            assertEquals(0,_pool.getNumActive());
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if one of those calls is unsupported
+        }
+    }
+
+    public void testBaseBorrowReturn() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        T obj0 = _pool.borrowObject();
+        assertEquals(getNthObject(0),obj0);
+        T obj1 = _pool.borrowObject();
+        assertEquals(getNthObject(1),obj1);
+        T obj2 = _pool.borrowObject();
+        assertEquals(getNthObject(2),obj2);
+        _pool.returnObject(obj2);
+        obj2 = _pool.borrowObject();
+        assertEquals(getNthObject(2),obj2);
+        _pool.returnObject(obj1);
+        obj1 = _pool.borrowObject();
+        assertEquals(getNthObject(1),obj1);
+        _pool.returnObject(obj0);
+        _pool.returnObject(obj2);
+        obj2 = _pool.borrowObject();
+        if (isLifo()) {
+            assertEquals(getNthObject(2),obj2);
+        }
+        if (isFifo()) {
+            assertEquals(getNthObject(0),obj2);
+        }
+
+        obj0 = _pool.borrowObject();
+        if (isLifo()) {
+            assertEquals(getNthObject(0),obj0);
+        }
+        if (isFifo()) {
+            assertEquals(getNthObject(2),obj0);
+        }
+    }
+
+    public void testBaseNumActiveNumIdle() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        T obj0 = _pool.borrowObject();
+        assertEquals(1,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        T obj1 = _pool.borrowObject();
+        assertEquals(2,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        _pool.returnObject(obj1);
+        assertEquals(1,_pool.getNumActive());
+        assertEquals(1,_pool.getNumIdle());
+        _pool.returnObject(obj0);
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(2,_pool.getNumIdle());
+    }
+
+    public void testBaseClear() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        T obj0 = _pool.borrowObject();
+        T obj1 = _pool.borrowObject();
+        assertEquals(2,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        _pool.returnObject(obj1);
+        _pool.returnObject(obj0);
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(2,_pool.getNumIdle());
+        _pool.clear();
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        Object obj2 = _pool.borrowObject();
+        assertEquals(getNthObject(2),obj2);
+    }
+
+    public void testBaseInvalidateObject() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        T obj0 = _pool.borrowObject();
+        T obj1 = _pool.borrowObject();
+        assertEquals(2,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        _pool.invalidateObject(obj0);
+        assertEquals(1,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+        _pool.invalidateObject(obj1);
+        assertEquals(0,_pool.getNumActive());
+        assertEquals(0,_pool.getNumIdle());
+    }
+
+    public void testBaseClosePool() throws Exception {
+        try {
+            _pool = makeEmptyPool(3);
+        } catch(UnsupportedOperationException e) {
+            return; // skip this test if unsupported
+        }
+        T obj = _pool.borrowObject();
+        _pool.returnObject(obj);
+
+        _pool.close();
+        try {
+            _pool.borrowObject();
+            fail("Expected IllegalStateException");
+        } catch(IllegalStateException e) {
+            // expected
+        }
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestBasePoolableObjectFactory.java b/src/test/org/apache/commons/pool/TestBasePoolableObjectFactory.java
new file mode 100644
index 0000000..839ee7a
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestBasePoolableObjectFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $ 
+ */
+public class TestBasePoolableObjectFactory extends TestCase {
+    public TestBasePoolableObjectFactory(String testName) {
+        super(testName);
+    }
+
+    public void testDefaultMethods() throws Exception {
+        PoolableObjectFactory<Object> factory = new BasePoolableObjectFactory<Object>() { 
+            @Override
+            public Object makeObject() throws Exception {
+                return null;
+            }
+        };   
+        
+        factory.activateObject(null); // a no-op
+        factory.passivateObject(null); // a no-op
+        factory.destroyObject(null); // a no-op
+        assertTrue(factory.validateObject(null)); // constant true
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestKeyedObjectPool.java b/src/test/org/apache/commons/pool/TestKeyedObjectPool.java
new file mode 100644
index 0000000..598c4a3
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestKeyedObjectPool.java
@@ -0,0 +1,521 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.pool.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool.impl.StackKeyedObjectPool;
+
+/**
+ * Abstract {@link TestCase} for {@link ObjectPool} implementations.
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
+ */
+public abstract class TestKeyedObjectPool extends TestCase {
+    public TestKeyedObjectPool(String testName) {
+        super(testName);
+    }
+
+    /**
+     * Create an <code>KeyedObjectPool</code> with the specified factory.
+     * The pool should be in a default configuration and conform to the expected
+     * behaviors described in {@link KeyedObjectPool}.
+     * Generally speaking there should be no limits on the various object counts.
+     */
+    protected abstract KeyedObjectPool<Object, Integer> makeEmptyPool(KeyedPoolableObjectFactory<Object, Integer> factory);
+
+    protected final String KEY = "key";
+
+    public void testClosedPoolBehavior() throws Exception {
+        final KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(new BaseKeyedPoolableObjectFactory<Object, Integer>() {
+                @Override
+                public Integer makeObject(final Object key) throws Exception {
+                    return new Integer(1234567890);
+                }
+            });
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+
+        Integer o1 = pool.borrowObject(KEY);
+        Integer o2 = pool.borrowObject(KEY);
+
+        pool.close();
+
+        try {
+            pool.addObject(KEY);
+            fail("A closed pool must throw an IllegalStateException when addObject is called.");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+
+        try {
+            pool.borrowObject(KEY);
+            fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+
+        // The following should not throw exceptions just because the pool is closed.
+        assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle(KEY));
+        assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle());
+        pool.getNumActive();
+        pool.getNumActive(KEY);
+        pool.returnObject(KEY, o1);
+        assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle(KEY));
+        assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle());
+        pool.invalidateObject(KEY, o2);
+        pool.clear(KEY);
+        pool.clear();
+        pool.close();
+    }
+
+    private final Integer ZERO = new Integer(0);
+    private final Integer ONE = new Integer(1);
+
+    public void testKPOFAddObjectUsage() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        final KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+
+        // addObject should make a new object, pasivate it and put it in the pool
+        pool.addObject(KEY);
+        expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO));
+        if (pool instanceof StackKeyedObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", KEY, ZERO).returned(Boolean.TRUE)); 
+        }
+        expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        //// Test exception handling of addObject
+        reset(pool, factory, expectedMethods);
+
+        // makeObject Exceptions should be propagated to client code from addObject
+        factory.setMakeObjectFail(true);
+        try {
+            pool.addObject(KEY);
+            fail("Expected addObject to propagate makeObject exception.");
+        } catch (PrivateException pe) {
+            // expected
+        }
+        expectedMethods.add(new MethodCall("makeObject", KEY));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        clear(factory, expectedMethods);
+
+        // passivateObject Exceptions should be propagated to client code from addObject
+        factory.setMakeObjectFail(false);
+        factory.setPassivateObjectFail(true);
+        try {
+            pool.addObject(KEY);
+            fail("Expected addObject to propagate passivateObject exception.");
+        } catch (PrivateException pe) {
+            // expected
+        }
+        expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+        if (pool instanceof StackKeyedObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", KEY, ONE).returned(Boolean.TRUE)); 
+        }
+        expectedMethods.add(new MethodCall("passivateObject", KEY, ONE));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+    }
+
+    public void testKPOFBorrowObjectUsages() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        final KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+        Integer obj;
+        
+        if (pool instanceof GenericKeyedObjectPool) {
+            ((GenericKeyedObjectPool<Object, Integer>) pool).setTestOnBorrow(true);
+        }
+
+        /// Test correct behavior code paths
+
+        // existing idle object should be activated and validated
+        pool.addObject(KEY);
+        clear(factory, expectedMethods);
+        obj = pool.borrowObject(KEY);
+        expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
+        expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+        pool.returnObject(KEY, obj);
+
+        //// Test exception handling of borrowObject
+        reset(pool, factory, expectedMethods);
+
+        // makeObject Exceptions should be propagated to client code from borrowObject
+        factory.setMakeObjectFail(true);
+        try {
+            obj = pool.borrowObject(KEY);
+            fail("Expected borrowObject to propagate makeObject exception.");
+        } catch (PrivateException pe) {
+            // expected
+        }
+        expectedMethods.add(new MethodCall("makeObject", KEY));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+
+        // when activateObject fails in borrowObject, a new object should be borrowed/created
+        reset(pool, factory, expectedMethods);
+        pool.addObject(KEY);
+        clear(factory, expectedMethods);
+
+        factory.setActivateObjectFail(true);
+        expectedMethods.add(new MethodCall("activateObject", KEY, obj));
+        try {
+            obj = pool.borrowObject(KEY); 
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException e) {
+            //Activate should fail
+        }
+        // After idle object fails validation, new on is created and activation
+        // fails again for the new one.
+        expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+        expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
+        TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        // when validateObject fails in borrowObject, a new object should be borrowed/created
+        reset(pool, factory, expectedMethods);
+        pool.addObject(KEY);
+        clear(factory, expectedMethods);
+
+        factory.setValidateObjectFail(true);
+        // testOnBorrow is on, so this will throw when the newly created instance
+        // fails validation
+        try {
+            obj = pool.borrowObject(KEY);
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // expected
+        }
+        // Activate, then validate for idle instance
+        expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
+        expectedMethods.add(new MethodCall("validateObject", KEY, ZERO));
+        // Make new instance, activate succeeds, validate fails
+        expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+        expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
+        expectedMethods.add(new MethodCall("validateObject", KEY, ONE));
+        TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
+        assertEquals(expectedMethods, factory.getMethodCalls());
+    }
+
+    public void testKPOFReturnObjectUsages() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        final KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+        Integer obj;
+
+        /// Test correct behavior code paths
+        obj = pool.borrowObject(KEY);
+        clear(factory, expectedMethods);
+
+        // returned object should be passivated
+        pool.returnObject(KEY, obj);
+        if (pool instanceof StackKeyedObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", KEY, obj).returned(Boolean.TRUE)); 
+        }
+        expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        //// Test exception handling of returnObject
+        reset(pool, factory, expectedMethods);
+
+        // passivateObject should swallow exceptions and not add the object to the pool
+        pool.addObject(KEY);
+        pool.addObject(KEY);
+        pool.addObject(KEY);
+        assertEquals(3, pool.getNumIdle(KEY));
+        obj = pool.borrowObject(KEY);
+        obj = pool.borrowObject(KEY);
+        assertEquals(1, pool.getNumIdle(KEY));
+        assertEquals(2, pool.getNumActive(KEY));
+        clear(factory, expectedMethods);
+        factory.setPassivateObjectFail(true);
+        pool.returnObject(KEY, obj);
+        if (pool instanceof StackKeyedObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", KEY, obj).returned(Boolean.TRUE)); 
+        }
+        expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
+        TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+        assertEquals(expectedMethods, factory.getMethodCalls());
+        assertEquals(1, pool.getNumIdle(KEY));   // Not added
+        assertEquals(1, pool.getNumActive(KEY)); // But not active
+
+        reset(pool, factory, expectedMethods);
+        obj = pool.borrowObject(KEY);
+        clear(factory, expectedMethods);
+        factory.setPassivateObjectFail(true);
+        factory.setDestroyObjectFail(true);
+        try {
+            pool.returnObject(KEY, obj);
+            if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat
+                fail("Expecting destroyObject exception to be propagated");
+            }
+        } catch (PrivateException ex) {
+            // Expected
+        }
+    }
+
+    public void testKPOFInvalidateObjectUsages() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        final KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+        Integer obj;
+
+        /// Test correct behavior code paths
+
+        obj = pool.borrowObject(KEY);
+        clear(factory, expectedMethods);
+
+        // invalidated object should be destroyed
+        pool.invalidateObject(KEY, obj);
+        expectedMethods.add(new MethodCall("destroyObject", KEY, obj));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        //// Test exception handling of invalidateObject
+        reset(pool, factory, expectedMethods);
+        obj = pool.borrowObject(KEY);
+        clear(factory, expectedMethods);
+        factory.setDestroyObjectFail(true);
+        try {
+            pool.invalidateObject(KEY, obj);
+            fail("Expecting destroy exception to propagate");
+        } catch (PrivateException ex) {
+            // Expected
+        }
+        Thread.sleep(250); // could be defered
+        TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
+        assertEquals(expectedMethods, factory.getMethodCalls());
+    }
+
+    public void testKPOFClearUsages() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        final KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+
+        /// Test correct behavior code paths
+        PoolUtils.prefill(pool, KEY, 5);
+        pool.clear();
+
+        //// Test exception handling clear should swallow destory object failures
+        reset(pool, factory, expectedMethods);
+        factory.setDestroyObjectFail(true);
+        PoolUtils.prefill(pool, KEY, 5);
+        pool.clear();
+    }
+
+    public void testKPOFCloseUsages() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        KeyedObjectPool<Object, Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+
+        /// Test correct behavior code paths
+        PoolUtils.prefill(pool, KEY, 5);
+        pool.close();
+
+
+        //// Test exception handling close should swallow failures
+        pool = makeEmptyPool(factory);
+        reset(pool, factory, expectedMethods);
+        factory.setDestroyObjectFail(true);
+        PoolUtils.prefill(pool, KEY, 5);
+        pool.close();
+    }
+
+    public void testToString() throws Exception {
+        final FailingKeyedPoolableObjectFactory factory = new FailingKeyedPoolableObjectFactory();
+        try {
+            makeEmptyPool(factory).toString();
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+    }
+
+    private void reset(final KeyedObjectPool<? ,?> pool, final FailingKeyedPoolableObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception {
+        pool.clear();
+        clear(factory, expectedMethods);
+        factory.reset();
+    }
+
+    private void clear(final FailingKeyedPoolableObjectFactory factory, final List<MethodCall> expectedMethods) {
+        factory.getMethodCalls().clear();
+        expectedMethods.clear();
+    }
+
+    protected static class FailingKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory<Object, Integer> {
+        private final List<MethodCall> methodCalls = new ArrayList<MethodCall>();
+        private int count = 0;
+        private boolean makeObjectFail;
+        private boolean activateObjectFail;
+        private boolean validateObjectFail;
+        private boolean passivateObjectFail;
+        private boolean destroyObjectFail;
+
+        public FailingKeyedPoolableObjectFactory() {
+        }
+
+        public void reset() {
+            count = 0;
+            getMethodCalls().clear();
+            setMakeObjectFail(false);
+            setActivateObjectFail(false);
+            setValidateObjectFail(false);
+            setPassivateObjectFail(false);
+            setDestroyObjectFail(false);
+        }
+
+        public List<MethodCall> getMethodCalls() {
+            return methodCalls;
+        }
+
+        public int getCurrentCount() {
+            return count;
+        }
+
+        public void setCurrentCount(final int count) {
+            this.count = count;
+        }
+
+        public boolean isMakeObjectFail() {
+            return makeObjectFail;
+        }
+
+        public void setMakeObjectFail(boolean makeObjectFail) {
+            this.makeObjectFail = makeObjectFail;
+        }
+
+        public boolean isDestroyObjectFail() {
+            return destroyObjectFail;
+        }
+
+        public void setDestroyObjectFail(boolean destroyObjectFail) {
+            this.destroyObjectFail = destroyObjectFail;
+        }
+
+        public boolean isValidateObjectFail() {
+            return validateObjectFail;
+        }
+
+        public void setValidateObjectFail(boolean validateObjectFail) {
+            this.validateObjectFail = validateObjectFail;
+        }
+
+        public boolean isActivateObjectFail() {
+            return activateObjectFail;
+        }
+
+        public void setActivateObjectFail(boolean activateObjectFail) {
+            this.activateObjectFail = activateObjectFail;
+        }
+
+        public boolean isPassivateObjectFail() {
+            return passivateObjectFail;
+        }
+
+        public void setPassivateObjectFail(boolean passivateObjectFail) {
+            this.passivateObjectFail = passivateObjectFail;
+        }
+
+        public Integer makeObject(final Object key) throws Exception {
+            final MethodCall call = new MethodCall("makeObject", key);
+            methodCalls.add(call);
+            int count = this.count++;
+            if (makeObjectFail) {
+                throw new PrivateException("makeObject");
+            }
+            final Integer obj = new Integer(count);
+            call.setReturned(obj);
+            return obj;
+        }
+
+        public void activateObject(final Object key, final Integer obj) throws Exception {
+            methodCalls.add(new MethodCall("activateObject", key, obj));
+            if (activateObjectFail) {
+                throw new PrivateException("activateObject");
+            }
+        }
+
+        public boolean validateObject(final Object key, final Integer obj) {
+            final MethodCall call = new MethodCall("validateObject", key, obj);
+            methodCalls.add(call);
+            if (validateObjectFail) {
+                throw new PrivateException("validateObject");
+            }
+            final boolean r = true;
+            call.returned(new Boolean(r));
+            return r;
+        }
+
+        public void passivateObject(final Object key, final Integer obj) throws Exception {
+            methodCalls.add(new MethodCall("passivateObject", key, obj));
+            if (passivateObjectFail) {
+                throw new PrivateException("passivateObject");
+            }
+        }
+
+        public void destroyObject(final Object key, final Integer obj) throws Exception {
+            methodCalls.add(new MethodCall("destroyObject", key, obj));
+            if (destroyObjectFail) {
+                throw new PrivateException("destroyObject");
+            }
+        }
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestKeyedObjectPoolFactory.java b/src/test/org/apache/commons/pool/TestKeyedObjectPoolFactory.java
new file mode 100644
index 0000000..f0ebc66
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestKeyedObjectPoolFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for all {@link KeyedObjectPoolFactory}s.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1221705 $ $Date: 2011-12-21 08:03:54 -0500 (Wed, 21 Dec 2011) $
+ */
+public abstract class TestKeyedObjectPoolFactory extends TestCase {
+    protected TestKeyedObjectPoolFactory(final String name) {
+        super(name);
+    }
+
+    /**
+     * @throws UnsupportedOperationException when this is unsupported by this KeyedPoolableObjectFactory type.
+     */
+    protected KeyedObjectPoolFactory<Object, Integer> makeFactory() throws UnsupportedOperationException {
+        return makeFactory(createObjectFactory());
+    }
+
+    /**
+     * @throws UnsupportedOperationException when this is unsupported by this KeyedPoolableObjectFactory type.
+     */
+    protected abstract KeyedObjectPoolFactory<Object, Integer> makeFactory(KeyedPoolableObjectFactory<Object, Integer> objectFactory) throws UnsupportedOperationException;
+
+    protected static KeyedPoolableObjectFactory<Object, Integer> createObjectFactory() {
+        return PoolUtils.adapt(new MethodCallPoolableObjectFactory());
+    }
+
+    public void testCreatePool() throws Exception {
+        final KeyedObjectPoolFactory<?, Integer> factory;
+        try {
+            factory = makeFactory();
+        } catch (UnsupportedOperationException uoe) {
+            return;
+        }
+        final KeyedObjectPool<?, Integer> pool = factory.createPool();
+        pool.close();
+    }
+
+    public void testToString() {
+        final KeyedObjectPoolFactory<?, Integer> factory;
+        try {
+            factory = makeFactory();
+        } catch (UnsupportedOperationException uoe) {
+            return;
+        }
+        factory.toString();
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestObjectPool.java b/src/test/org/apache/commons/pool/TestObjectPool.java
new file mode 100644
index 0000000..5f6653b
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestObjectPool.java
@@ -0,0 +1,439 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.commons.pool.impl.SoftReferenceObjectPool;
+import org.apache.commons.pool.impl.StackObjectPool;
+
+/**
+ * Abstract {@link TestCase} for {@link ObjectPool} implementations.
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
+ */
+public abstract class TestObjectPool extends TestCase {
+    public TestObjectPool(String testName) {
+        super(testName);
+    }
+
+    /**
+     * Create an <code>ObjectPool</code> with the specified factory.
+     * The pool should be in a default configuration and conform to the expected
+     * behaviors described in {@link ObjectPool}.
+     * Generally speaking there should be no limits on the various object counts.
+     * @throws UnsupportedOperationException if the pool being tested does not follow pool contracts.
+     */
+    protected abstract ObjectPool<Integer> makeEmptyPool(PoolableObjectFactory<Integer> factory) throws UnsupportedOperationException;
+
+    public void testClosedPoolBehavior() throws Exception {
+        final ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        Integer o1 = pool.borrowObject();
+        Integer o2 = pool.borrowObject();
+
+        pool.close();
+
+        try {
+            pool.addObject();
+            fail("A closed pool must throw an IllegalStateException when addObject is called.");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+
+        try {
+            pool.borrowObject();
+            fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+
+        // The following should not throw exceptions just because the pool is closed.
+        if (pool.getNumIdle() >= 0) {
+            assertEquals("A closed pool shouldn't have any idle objects.", 0, pool.getNumIdle());
+        }
+        if (pool.getNumActive() >= 0) {
+            assertEquals("A closed pool should still keep count of active objects.", 2, pool.getNumActive());
+        }
+        pool.returnObject(o1);
+        if (pool.getNumIdle() >= 0) {
+            assertEquals("returnObject should not add items back into the idle object pool for a closed pool.", 0, pool.getNumIdle());
+        }
+        if (pool.getNumActive() >= 0) {
+            assertEquals("A closed pool should still keep count of active objects.", 1, pool.getNumActive());
+        }
+        pool.invalidateObject(o2);
+        if (pool.getNumIdle() >= 0) {
+            assertEquals("invalidateObject must not add items back into the idle object pool.", 0, pool.getNumIdle());
+        }
+        if (pool.getNumActive() >= 0) {
+            assertEquals("A closed pool should still keep count of active objects.", 0, pool.getNumActive());
+        }
+        pool.clear();
+        pool.close();
+    }
+
+    private final Integer ZERO = new Integer(0);
+    private final Integer ONE = new Integer(1);
+
+    public void testPOFAddObjectUsage() throws Exception {
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        final ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch(UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+
+        assertEquals(0, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+        // addObject should make a new object, pasivate it and put it in the pool
+        pool.addObject();
+        assertEquals(0, pool.getNumActive());
+        assertEquals(1, pool.getNumIdle());
+        expectedMethods.add(new MethodCall("makeObject").returned(ZERO));
+        // StackObjectPool, SoftReferenceObjectPool also validate on add
+        if (pool instanceof StackObjectPool || 
+                pool instanceof SoftReferenceObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", ZERO).returned(Boolean.TRUE));
+        }
+        expectedMethods.add(new MethodCall("passivateObject", ZERO));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        //// Test exception handling of addObject
+        reset(pool, factory, expectedMethods);
+
+        // makeObject Exceptions should be propagated to client code from addObject
+        factory.setMakeObjectFail(true);
+        try {
+            pool.addObject();
+            fail("Expected addObject to propagate makeObject exception.");
+        } catch (PrivateException pe) {
+            // expected
+        }
+        expectedMethods.add(new MethodCall("makeObject"));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        clear(factory, expectedMethods);
+
+        // passivateObject Exceptions should be propagated to client code from addObject
+        factory.setMakeObjectFail(false);
+        factory.setPassivateObjectFail(true);
+        try {
+            pool.addObject();
+            fail("Expected addObject to propagate passivateObject exception.");
+        } catch (PrivateException pe) {
+            // expected
+        }
+        expectedMethods.add(new MethodCall("makeObject").returned(ONE));
+        // StackObjectPool, SofReferenceObjectPool also validate on add
+        if (pool instanceof StackObjectPool || 
+                pool instanceof SoftReferenceObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", ONE).returned(Boolean.TRUE));
+        }
+        expectedMethods.add(new MethodCall("passivateObject", ONE));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+    }
+
+    public void testPOFBorrowObjectUsages() throws Exception {
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        final ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        if (pool instanceof GenericObjectPool) {
+            ((GenericObjectPool<Integer>) pool).setTestOnBorrow(true);
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+        Integer obj;
+
+        /// Test correct behavior code paths
+
+        // existing idle object should be activated and validated
+        pool.addObject();
+        clear(factory, expectedMethods);
+        obj = pool.borrowObject();
+        expectedMethods.add(new MethodCall("activateObject", ZERO));
+        expectedMethods.add(new MethodCall("validateObject", ZERO).returned(Boolean.TRUE));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+        pool.returnObject(obj);
+
+        //// Test exception handling of borrowObject
+        reset(pool, factory, expectedMethods);
+
+        // makeObject Exceptions should be propagated to client code from borrowObject
+        factory.setMakeObjectFail(true);
+        try {
+            obj = pool.borrowObject();
+            fail("Expected borrowObject to propagate makeObject exception.");
+        } catch (PrivateException pe) {
+            // expected
+        }
+        expectedMethods.add(new MethodCall("makeObject"));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+
+        // when activateObject fails in borrowObject, a new object should be borrowed/created
+        reset(pool, factory, expectedMethods);
+        pool.addObject();
+        clear(factory, expectedMethods);
+
+        factory.setActivateObjectFail(true);
+        expectedMethods.add(new MethodCall("activateObject", obj));
+        try {
+            obj = pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // Expected - newly created object will also fail to activate
+        }
+        // Idle object fails activation, new one created, also fails
+        expectedMethods.add(new MethodCall("makeObject").returned(ONE));
+        expectedMethods.add(new MethodCall("activateObject", ONE));
+        removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        // when validateObject fails in borrowObject, a new object should be borrowed/created
+        reset(pool, factory, expectedMethods);
+        pool.addObject();
+        clear(factory, expectedMethods);
+
+        factory.setValidateObjectFail(true);
+        expectedMethods.add(new MethodCall("activateObject", ZERO));
+        expectedMethods.add(new MethodCall("validateObject", ZERO));
+        try {
+            obj = pool.borrowObject();
+        } catch (NoSuchElementException ex) {
+            // Expected - newly created object will also fail to validate
+        }
+        // Idle object is activated, but fails validation.
+        // New instance is created, activated and then fails validation
+        expectedMethods.add(new MethodCall("makeObject").returned(ONE));
+        expectedMethods.add(new MethodCall("activateObject", ONE));
+        expectedMethods.add(new MethodCall("validateObject", ONE));
+        removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+        // Second activate and validate are missing from expectedMethods
+        assertTrue(factory.getMethodCalls().containsAll(expectedMethods));
+    }
+
+    public void testPOFReturnObjectUsages() throws Exception {
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        final ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+        Integer obj;
+
+        /// Test correct behavior code paths
+        obj = pool.borrowObject();
+        clear(factory, expectedMethods);
+
+        // returned object should be passivated
+        pool.returnObject(obj);
+        // StackObjectPool, SoftReferenceObjectPool also validate on return
+        if (pool instanceof StackObjectPool || 
+                pool instanceof SoftReferenceObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", obj).returned(Boolean.TRUE));
+        }
+        expectedMethods.add(new MethodCall("passivateObject", obj));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        //// Test exception handling of returnObject
+        reset(pool, factory, expectedMethods);
+        pool.addObject();
+        pool.addObject();
+        pool.addObject();
+        assertEquals(3, pool.getNumIdle());
+        // passivateObject should swallow exceptions and not add the object to the pool
+        obj = pool.borrowObject();
+        pool.borrowObject();
+        assertEquals(1, pool.getNumIdle());
+        assertEquals(2, pool.getNumActive());
+        clear(factory, expectedMethods);
+        factory.setPassivateObjectFail(true);
+        pool.returnObject(obj);
+        // StackObjectPool, SoftReferenceObjectPool also validate on return
+        if (pool instanceof StackObjectPool || 
+                pool instanceof SoftReferenceObjectPool) {
+            expectedMethods.add(new MethodCall(
+                    "validateObject", obj).returned(Boolean.TRUE));
+        }
+        expectedMethods.add(new MethodCall("passivateObject", obj));
+        removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+        assertEquals(expectedMethods, factory.getMethodCalls());
+        assertEquals(1, pool.getNumIdle());   // Not returned
+        assertEquals(1, pool.getNumActive()); // But not in active count
+
+        // destroyObject should swallow exceptions too
+        reset(pool, factory, expectedMethods);
+        obj = pool.borrowObject();
+        clear(factory, expectedMethods);
+        factory.setPassivateObjectFail(true);
+        factory.setDestroyObjectFail(true);
+        pool.returnObject(obj);
+    }
+
+    public void testPOFInvalidateObjectUsages() throws Exception {
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        final ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+        Integer obj;
+
+        /// Test correct behavior code paths
+
+        obj = pool.borrowObject();
+        clear(factory, expectedMethods);
+
+        // invalidated object should be destroyed
+        pool.invalidateObject(obj);
+        expectedMethods.add(new MethodCall("destroyObject", obj));
+        assertEquals(expectedMethods, factory.getMethodCalls());
+
+        //// Test exception handling of invalidateObject
+        reset(pool, factory, expectedMethods);
+        obj = pool.borrowObject();
+        clear(factory, expectedMethods);
+        factory.setDestroyObjectFail(true);
+        try {
+            pool.invalidateObject(obj);
+            fail("Expecting destroy exception to propagate");
+        } catch (PrivateException ex) {
+            // Expected
+        }
+        Thread.sleep(250); // could be defered
+        removeDestroyObjectCall(factory.getMethodCalls());
+        assertEquals(expectedMethods, factory.getMethodCalls());
+    }
+
+    public void testPOFClearUsages() throws Exception {
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        final ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+
+        /// Test correct behavior code paths
+        PoolUtils.prefill(pool, 5);
+        pool.clear();
+
+        //// Test exception handling clear should swallow destory object failures
+        reset(pool, factory, expectedMethods);
+        factory.setDestroyObjectFail(true);
+        PoolUtils.prefill(pool, 5);
+        pool.clear();
+    }
+
+    public void testPOFCloseUsages() throws Exception {
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final List<MethodCall> expectedMethods = new ArrayList<MethodCall>();
+
+        /// Test correct behavior code paths
+        PoolUtils.prefill(pool, 5);
+        pool.close();
+
+
+        //// Test exception handling close should swallow failures
+        try {
+            pool = makeEmptyPool(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        reset(pool, factory, expectedMethods);
+        factory.setDestroyObjectFail(true);
+        PoolUtils.prefill(pool, 5);
+        pool.close();
+    }
+
+    public void testSetFactory() throws Exception {
+        ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+        try {
+            pool.setFactory(factory);
+        } catch (UnsupportedOperationException uoe) {
+            return;
+        }
+    }
+
+    public void testToString() {
+        ObjectPool<Integer> pool;
+        try {
+            pool = makeEmptyPool(new MethodCallPoolableObjectFactory());
+        } catch (UnsupportedOperationException uoe) {
+            return; // test not supported
+        }
+        pool.toString();
+    }
+
+    static void removeDestroyObjectCall(List<MethodCall> calls) {
+        Iterator<MethodCall> iter = calls.iterator();
+        while (iter.hasNext()) {
+            MethodCall call = iter.next();
+            if ("destroyObject".equals(call.getName())) {
+                iter.remove();
+            }
+        }
+    }
+
+    private static void reset(final ObjectPool<?> pool, final MethodCallPoolableObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception {
+        pool.clear();
+        clear(factory, expectedMethods);
+        factory.reset();
+    }
+
+    private static void clear(final MethodCallPoolableObjectFactory factory, final List<MethodCall> expectedMethods) {
+        factory.getMethodCalls().clear();
+        expectedMethods.clear();
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestObjectPoolFactory.java b/src/test/org/apache/commons/pool/TestObjectPoolFactory.java
new file mode 100644
index 0000000..534ee73
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestObjectPoolFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for all {@link ObjectPoolFactory}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1221705 $ $Date: 2011-12-21 08:03:54 -0500 (Wed, 21 Dec 2011) $
+ */
+public abstract class TestObjectPoolFactory extends TestCase {
+    protected TestObjectPoolFactory(final String name) {
+        super(name);
+    }
+
+    /**
+     * @throws UnsupportedOperationException when this is unsupported by this PoolableObjectFactory type.
+     */
+    protected ObjectPoolFactory<Integer> makeFactory() throws UnsupportedOperationException {
+        return makeFactory(new MethodCallPoolableObjectFactory());
+    }
+
+    /**
+     * @throws UnsupportedOperationException when this is unsupported by this PoolableObjectFactory type.
+     */
+    protected abstract ObjectPoolFactory<Integer> makeFactory(PoolableObjectFactory<Integer> objectFactory) throws UnsupportedOperationException;
+
+    public void testCreatePool() throws Exception {
+        final ObjectPoolFactory<Integer> factory;
+        try {
+            factory = makeFactory();
+        } catch (UnsupportedOperationException uoe) {
+            return;
+        }
+        final ObjectPool<Integer> pool = factory.createPool();
+        pool.close();
+    }
+
+    public void testToString() {
+        final ObjectPoolFactory<Integer> factory;
+        try {
+            factory = makeFactory();
+        } catch (UnsupportedOperationException uoe) {
+            return;
+        }
+        factory.toString();
+    }
+}
diff --git a/src/test/org/apache/commons/pool/TestPoolUtils.java b/src/test/org/apache/commons/pool/TestPoolUtils.java
new file mode 100644
index 0000000..28974a9
--- /dev/null
+++ b/src/test/org/apache/commons/pool/TestPoolUtils.java
@@ -0,0 +1,999 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimerTask;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.commons.pool.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool.impl.GenericObjectPool;
+
+/**
+ * Unit tests for {@link PoolUtils}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestPoolUtils extends TestCase {
+
+    /** Period between checks for minIdle tests. Increase this if you happen to get too many false failures. */
+    private static final int CHECK_PERIOD = 300;
+
+    /** Times to let the minIdle check run. */
+    private static final int CHECK_COUNT = 4;
+
+    /** Sleep time to let the minIdle tests run CHECK_COUNT times. */
+    private static final int CHECK_SLEEP_PERIOD = CHECK_PERIOD * (CHECK_COUNT - 1) + CHECK_PERIOD / 2;
+
+    public void testJavaBeanInstantiation() {
+        new PoolUtils();
+    }
+
+    public void testAdaptKeyedPoolableObjectFactory() throws Exception {
+        try {
+            PoolUtils.adapt((KeyedPoolableObjectFactory<Object, Object>)null);
+            fail("PoolUtils.adapt(KeyedPoolableObjectFactory) must not allow null factory.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+    }
+
+    public void testAdaptKeyedPoolableObjectFactoryKey() throws Exception {
+        try {
+            PoolUtils.adapt((KeyedPoolableObjectFactory<Object, Object>)null, new Object());
+            fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null factory.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedPoolableObjectFactory<Object, Object> proxy = createProxy(KeyedPoolableObjectFactory.class, (List<String>)null);
+            PoolUtils.adapt(proxy, null);
+            fail("PoolUtils.adapt(KeyedPoolableObjectFactory, key) must not allow null key.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final KeyedPoolableObjectFactory<Object, Object> kpof =
+                createProxy(KeyedPoolableObjectFactory.class, calledMethods);
+
+        final PoolableObjectFactory<Object> pof = PoolUtils.adapt(kpof);
+        final List<String> expectedMethods = invokeEveryMethod(pof);
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testAdaptPoolableObjectFactory() throws Exception {
+        try {
+            PoolUtils.adapt((PoolableObjectFactory<?>)null);
+            fail("PoolUtils.adapt(PoolableObjectFactory) must not allow null factory.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        final PoolableObjectFactory<?> pof =
+                createProxy(PoolableObjectFactory.class, calledMethods);
+
+        final KeyedPoolableObjectFactory<?, ?> kpof = PoolUtils.adapt(pof);
+        final List<String> expectedMethods = invokeEveryMethod(kpof);
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testAdaptKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.adapt((KeyedObjectPool<Object ,?>)null);
+            fail("PoolUtils.adapt(KeyedObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+    }
+
+    public void testAdaptKeyedObjectPoolKey() throws Exception {
+        try {
+            PoolUtils.adapt((KeyedObjectPool<Object, Object>)null, new Object());
+            fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedObjectPool<Object, Object> proxy = createProxy(KeyedObjectPool.class, (List<String>)null);
+            PoolUtils.adapt(proxy, null);
+            fail("PoolUtils.adapt(KeyedObjectPool, key) must not allow a null key.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Object, Object> kop = createProxy(KeyedObjectPool.class, calledMethods);
+
+        final ObjectPool<Object> op = PoolUtils.adapt(kop, new Object());
+        final List<String> expectedMethods = invokeEveryMethod(op);
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testAdaptObjectPool() throws Exception {
+        try {
+            PoolUtils.adapt((ObjectPool<?>)null);
+            fail("PoolUtils.adapt(ObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods);
+
+        final KeyedObjectPool<Object, Object> kop = PoolUtils.adapt(op);
+        final List<String> expectedMethods = invokeEveryMethod(kop);
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testCheckedPoolObjectPool() throws Exception {
+        try {
+            PoolUtils.checkedPool((ObjectPool<Object>)null, Object.class);
+            fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            final ObjectPool<?> proxy = createProxy(ObjectPool.class, (List<String>)null);
+            PoolUtils.checkedPool(proxy, null);
+            fail("PoolUtils.checkedPool(ObjectPool, Class) must not allow a null type.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods);
+
+        ObjectPool<Object> cop = PoolUtils.checkedPool(op, Object.class);
+        final List<String> expectedMethods = invokeEveryMethod(cop);
+        assertEquals(expectedMethods, calledMethods);
+
+        op = new BaseObjectPool<Object>() {
+            @Override
+            public Object borrowObject() throws Exception {
+                return new Integer(0);
+            }
+            @Override
+            public void returnObject(Object obj) {}
+            @Override
+            public void invalidateObject(Object obj) {}
+        };
+        // cast to ObjectPool to be able to use String.class
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        final ObjectPool checkedPool = PoolUtils.checkedPool((ObjectPool)op, String.class);
+        @SuppressWarnings("unchecked")
+        final ObjectPool<Object> cop2 = checkedPool;
+
+        try {
+            cop2.borrowObject();
+            fail("borrowObject should have failed as Integer !instanceof String.");
+        } catch (ClassCastException cce) {
+            // expected
+        }
+        try {
+            cop2.returnObject(new Integer(1));
+            fail("returnObject should have failed as Integer !instanceof String.");
+        } catch (ClassCastException cce) {
+            // expected
+        }
+        try {
+            cop2.invalidateObject(new Integer(2));
+            fail("invalidateObject should have failed as Integer !instanceof String.");
+        } catch (ClassCastException cce) {
+            // expected
+        }
+    }
+
+    public void testCheckedPoolKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.checkedPool((KeyedObjectPool<Object, Object>)null, Object.class);
+            fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            PoolUtils.checkedPool((KeyedObjectPool<?, ?>)createProxy(KeyedObjectPool.class, (List<String>)null), null);
+            fail("PoolUtils.checkedPool(KeyedObjectPool, Class) must not allow a null type.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        KeyedObjectPool<Object, Object> op = createProxy(KeyedObjectPool.class, calledMethods);
+
+        KeyedObjectPool<Object, Object> cop = PoolUtils.checkedPool(op, Object.class);
+        final List<String> expectedMethods = invokeEveryMethod(cop);
+        assertEquals(expectedMethods, calledMethods);
+
+
+        op = new BaseKeyedObjectPool<Object, Object>() {
+            @Override
+            public Integer borrowObject(Object key) {
+                return new Integer(0);
+            }
+
+            @Override
+            public void returnObject(Object key, Object obj) {}
+
+            @Override
+            public void invalidateObject(Object key, Object obj) {}
+        };
+        @SuppressWarnings("rawtypes")
+        final KeyedObjectPool rawPool = op;
+        @SuppressWarnings("unchecked")
+        KeyedObjectPool<Object, Object> cop2 = PoolUtils.checkedPool(rawPool, String.class);
+
+        try {
+            cop2.borrowObject(null);
+            fail("borrowObject should have failed as Integer !instanceof String.");
+        } catch (ClassCastException cce) {
+            // expected
+        }
+        try {
+            cop2.returnObject(null, new Integer(1));
+            fail("returnObject should have failed as Integer !instanceof String.");
+        } catch (ClassCastException cce) {
+            // expected
+        }
+        try {
+            cop2.invalidateObject(null, new Integer(2));
+            fail("invalidateObject should have failed as Integer !instanceof String.");
+        } catch (ClassCastException cce) {
+            // expected
+        }
+    }
+
+    public void testCheckMinIdleObjectPool() throws Exception {
+        try {
+            PoolUtils.checkMinIdle(null, 1, 1);
+            fail("PoolUtils.checkMinIdle(ObjectPool,,) must not allow null pool.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            final ObjectPool<?> pool = createProxy(ObjectPool.class, (List<String>)null);
+            PoolUtils.checkMinIdle(pool, -1, 1);
+            fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept negative min idle values.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+
+        // Test that the minIdle check doesn't add too many idle objects
+        @SuppressWarnings("unchecked")
+        final PoolableObjectFactory<Object> pof = createProxy(PoolableObjectFactory.class, calledMethods);
+        final ObjectPool<Object> op = new GenericObjectPool<Object>(pof);
+        PoolUtils.checkMinIdle(op, 2, 100);
+        Thread.sleep(400);
+        assertEquals(2, op.getNumIdle());
+        op.close();
+        int makeObjectCount = 0;
+        final Iterator<String> iter = calledMethods.iterator();
+        while (iter.hasNext()) {
+            final String methodName = iter.next();
+            if ("makeObject".equals(methodName)) {
+                makeObjectCount++;
+            }
+        }
+        assertEquals("makeObject should have been called two time", 2, makeObjectCount);
+
+        // Because this isn't deterministic and you can get false failures, try more than once.
+        AssertionFailedError afe = null;
+        int triesLeft = 3;
+        do {
+            afe = null;
+            try {
+                calledMethods.clear();
+                @SuppressWarnings("unchecked")
+                final ObjectPool<Object> pool = createProxy(ObjectPool.class, calledMethods);
+                final TimerTask task = PoolUtils.checkMinIdle(pool, 1, CHECK_PERIOD); // checks minIdle immediately
+
+                Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
+                task.cancel();
+                task.toString();
+
+                final List<String> expectedMethods = new ArrayList<String>();
+                for (int i=0; i < CHECK_COUNT; i++) {
+                    expectedMethods.add("getNumIdle");
+                    expectedMethods.add("addObject");
+                }
+                expectedMethods.add("toString");
+                assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
+            } catch (AssertionFailedError e) {
+                afe = e;
+            }
+        } while (--triesLeft > 0 && afe != null);
+        if (afe != null) {
+            throw afe;
+        }
+    }
+
+    public void testCheckMinIdleKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.checkMinIdle(null, new Object(), 1, 1);
+            fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not allow null pool.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null);
+            PoolUtils.checkMinIdle(pool, (Object)null, 1, 1);
+            fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not accept null keys.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null);
+            PoolUtils.checkMinIdle(pool, new Object(), -1, 1);
+            fail("PoolUtils.checkMinIdle(KeyedObjectPool,Object,int,long) must not accept negative min idle values.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        final Object key = new Object();
+
+        // Test that the minIdle check doesn't add too many idle objects
+        @SuppressWarnings("unchecked")
+        final KeyedPoolableObjectFactory<Object, Object> kpof = createProxy(KeyedPoolableObjectFactory.class, calledMethods);
+        final KeyedObjectPool<Object, Object> kop = new GenericKeyedObjectPool<Object, Object>(kpof);
+        PoolUtils.checkMinIdle(kop, key, 2, 100);
+        Thread.sleep(400);
+        assertEquals(2, kop.getNumIdle(key));
+        assertEquals(2, kop.getNumIdle());
+        kop.close();
+        int makeObjectCount = 0;
+        final Iterator<String> iter = calledMethods.iterator();
+        while (iter.hasNext()) {
+            final String methodName = iter.next();
+            if ("makeObject".equals(methodName)) {
+                makeObjectCount++;
+            }
+        }
+        assertEquals("makeObject should have been called two time", 2, makeObjectCount);
+
+        // Because this isn't deterministic and you can get false failures, try more than once.
+        AssertionFailedError afe = null;
+        int triesLeft = 3;
+        do {
+            afe = null;
+            try {
+                calledMethods.clear();
+                @SuppressWarnings("unchecked")
+                final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, calledMethods);
+                final TimerTask task = PoolUtils.checkMinIdle(pool, key, 1, CHECK_PERIOD); // checks minIdle immediately
+
+                Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
+                task.cancel();
+                task.toString();
+
+                final List<String> expectedMethods = new ArrayList<String>();
+                for (int i=0; i < CHECK_COUNT; i++) {
+                    expectedMethods.add("getNumIdle");
+                    expectedMethods.add("addObject");
+                }
+                expectedMethods.add("toString");
+                assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
+            } catch (AssertionFailedError e) {
+                afe = e;
+            }
+        } while (--triesLeft > 0 && afe != null);
+        if (afe != null) {
+            throw afe;
+        }
+    }
+
+    public void testCheckMinIdleKeyedObjectPoolKeys() throws Exception {
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedObjectPool<String, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null);
+            PoolUtils.checkMinIdle(pool, (Collection<String>)null, 1, 1);
+            fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must not accept null keys.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        // Because this isn't determinist and you can get false failures, try more than once.
+        AssertionFailedError afe = null;
+        int triesLeft = 3;
+        do {
+            afe = null;
+            try {
+                final List<String> calledMethods = new ArrayList<String>();
+                @SuppressWarnings("unchecked")
+                final KeyedObjectPool<String, ?> pool = createProxy(KeyedObjectPool.class, calledMethods);
+                final Collection<String> keys = new ArrayList<String>(2);
+                keys.add("one");
+                keys.add("two");
+                final Map<String, TimerTask> tasks = PoolUtils.checkMinIdle(pool, keys, 1, CHECK_PERIOD); // checks minIdle immediately
+
+                Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
+                final Iterator<TimerTask> iter = tasks.values().iterator();
+                while (iter.hasNext()) {
+                    final TimerTask task = iter.next();
+                    task.cancel();
+                }
+
+                final List<String> expectedMethods = new ArrayList<String>();
+                for (int i=0; i < CHECK_COUNT * keys.size(); i++) {
+                    expectedMethods.add("getNumIdle");
+                    expectedMethods.add("addObject");
+                }
+                assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
+            } catch (AssertionFailedError e) {
+                afe = e;
+            }
+        } while (--triesLeft > 0 && afe != null);
+        if (afe != null) {
+            throw afe;
+        }
+    }
+
+    public void testPrefillObjectPool() throws Exception {
+        try {
+            PoolUtils.prefill(null, 1);
+            fail("PoolUtils.prefill(ObjectPool,int) must not allow null pool.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        final ObjectPool<?> pool = createProxy(ObjectPool.class, calledMethods);
+
+        PoolUtils.prefill(pool, 0);
+        final List<String> expectedMethods = new ArrayList<String>();
+        assertEquals(expectedMethods, calledMethods);
+
+        calledMethods.clear();
+        PoolUtils.prefill(pool, 3);
+        for (int i=0; i < 3; i++) {
+            expectedMethods.add("addObject");
+        }
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testPrefillKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.prefill(null, new Object(), 1);
+            fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null pool.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null);
+            PoolUtils.prefill(pool, (Object)null, 1);
+            fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null key.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, calledMethods);
+
+        PoolUtils.prefill(pool, new Object(), 0);
+        final List<String> expectedMethods = new ArrayList<String>();
+        assertEquals(expectedMethods, calledMethods);
+
+        calledMethods.clear();
+        PoolUtils.prefill(pool, new Object(), 3);
+        for (int i=0; i < 3; i++) {
+            expectedMethods.add("addObject");
+        }
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testPrefillKeyedObjectPoolCollection() throws Exception {
+        try {
+            @SuppressWarnings("unchecked")
+            final KeyedObjectPool<String, Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null);
+            PoolUtils.prefill(pool, (Collection<String>)null, 1);
+            fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not accept null keys.");
+        } catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Number, Object> pool = createProxy(KeyedObjectPool.class, calledMethods);
+
+        final Set<Number> keys = new HashSet<Number>();
+        PoolUtils.prefill(pool, keys, 0);
+        final List<String> expectedMethods = new ArrayList<String>();
+        assertEquals(expectedMethods, calledMethods);
+
+        calledMethods.clear();
+        keys.add(new Integer(1));
+        keys.add(new Long(2));
+        keys.add(new Double(3.1415926));
+        PoolUtils.prefill(pool, keys, 3);
+        for (int i=0; i < keys.size() * 3; i++) {
+            expectedMethods.add("addObject");
+        }
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testSynchronizedPoolObjectPool() throws Exception {
+        try {
+            PoolUtils.synchronizedPool((ObjectPool<Object>)null);
+            fail("PoolUtils.synchronizedPool(ObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods);
+
+        final ObjectPool<Object> sop = PoolUtils.synchronizedPool(op);
+        final List<String> expectedMethods = invokeEveryMethod(sop);
+        assertEquals(expectedMethods, calledMethods);
+
+        // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+    }
+
+    public void testSynchronizedPoolKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.synchronizedPool((KeyedObjectPool<Object, Object>)null);
+            fail("PoolUtils.synchronizedPool(KeyedObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Object, Object> kop = createProxy(KeyedObjectPool.class, calledMethods);
+
+        final KeyedObjectPool<Object, Object> skop = PoolUtils.synchronizedPool(kop);
+        final List<String> expectedMethods = invokeEveryMethod(skop);
+        assertEquals(expectedMethods, calledMethods);
+
+        // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+    }
+
+    public void testSynchronizedPoolableFactoryPoolableObjectFactory() throws Exception {
+        try {
+            PoolUtils.synchronizedPoolableFactory((PoolableObjectFactory<Object>)null);
+            fail("PoolUtils.synchronizedPoolableFactory(PoolableObjectFactory) must not allow a null factory.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final PoolableObjectFactory<Object> pof =
+                createProxy(PoolableObjectFactory.class, calledMethods);
+
+        final PoolableObjectFactory<Object> spof = PoolUtils.synchronizedPoolableFactory(pof);
+        final List<String> expectedMethods = invokeEveryMethod(spof);
+        assertEquals(expectedMethods, calledMethods);
+
+        // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+    }
+
+    public void testSynchronizedPoolableFactoryKeyedPoolableObjectFactory() throws Exception {
+        try {
+            PoolUtils.synchronizedPoolableFactory((KeyedPoolableObjectFactory<Object, Object>)null);
+            fail("PoolUtils.synchronizedPoolableFactory(KeyedPoolableObjectFactory) must not allow a null factory.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        @SuppressWarnings("unchecked")
+        final KeyedPoolableObjectFactory<Object, Object> kpof =
+                createProxy(KeyedPoolableObjectFactory.class, calledMethods);
+
+        final KeyedPoolableObjectFactory<Object, Object> skpof = PoolUtils.synchronizedPoolableFactory(kpof);
+        final List<String> expectedMethods = invokeEveryMethod(skpof);
+        assertEquals(expectedMethods, calledMethods);
+
+        // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+    }
+
+    public void testErodingPoolObjectPool() throws Exception {
+        try {
+            PoolUtils.erodingPool((ObjectPool<Object>)null);
+            fail("PoolUtils.erodingPool(ObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((ObjectPool<Object>)null, 1f);
+            fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((ObjectPool<Object>)null, 0);
+            fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        final InvocationHandler handler = new MethodCallLogger(calledMethods) {
+            @Override
+            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+                Object o = super.invoke(proxy, method, args);
+                if (o instanceof Integer) {
+                    // so getNumActive/getNumIdle are not zero.
+                    o = new Integer(1);
+                }
+                return o;
+            }
+        };
+
+        // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
+        float factor = 0.01f; // about ~9 seconds until first discard
+        @SuppressWarnings("unchecked")
+        final ObjectPool<Object> pool = PoolUtils.erodingPool((ObjectPool<Object>)createProxy(ObjectPool.class, handler), factor);
+
+        final List<String> expectedMethods = new ArrayList<String>();
+        assertEquals(expectedMethods, calledMethods);
+
+        Object o = pool.borrowObject();
+        expectedMethods.add("borrowObject");
+
+        assertEquals(expectedMethods, calledMethods);
+
+        pool.returnObject(o);
+        expectedMethods.add("returnObject");
+        assertEquals(expectedMethods, calledMethods);
+
+        for (int i=0; i < 5; i ++) {
+            o = pool.borrowObject();
+            expectedMethods.add("borrowObject");
+
+            Thread.sleep(50);
+
+            pool.returnObject(o);
+            expectedMethods.add("returnObject");
+
+            assertEquals(expectedMethods, calledMethods);
+
+            expectedMethods.clear();
+            calledMethods.clear();
+        }
+
+        Thread.sleep(10000); // 10 seconds
+
+        
+        o = pool.borrowObject();
+        expectedMethods.add("borrowObject");
+        pool.returnObject(o);
+        expectedMethods.add("getNumIdle");
+        expectedMethods.add("invalidateObject");
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    public void testErodingPoolKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null);
+            fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1f);
+            fail("PoolUtils.erodingPool(KeyedObjectPool, float) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 0);
+            fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1f, true);
+            fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 0, false);
+            fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        final InvocationHandler handler = new MethodCallLogger(calledMethods) {
+            @Override
+            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+                Object o = super.invoke(proxy, method, args);
+                if (o instanceof Integer) {
+                    // so getNumActive/getNumIdle are not zero.
+                    o = new Integer(1);
+                }
+                return o;
+            }
+        };
+
+        // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
+        float factor = 0.01f; // about ~9 seconds until first discard
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Object, Object> pool = PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)createProxy(KeyedObjectPool.class, handler), factor);
+
+        final List<String> expectedMethods = new ArrayList<String>();
+        assertEquals(expectedMethods, calledMethods);
+
+        final Object key = "key";
+
+        Object o = pool.borrowObject(key);
+        expectedMethods.add("borrowObject");
+
+        assertEquals(expectedMethods, calledMethods);
+
+        pool.returnObject(key, o);
+        expectedMethods.add("returnObject");
+        assertEquals(expectedMethods, calledMethods);
+
+        for (int i=0; i < 5; i ++) {
+            o = pool.borrowObject(key);
+            expectedMethods.add("borrowObject");
+
+            Thread.sleep(50);
+
+            pool.returnObject(key, o);
+            expectedMethods.add("returnObject");
+
+            assertEquals(expectedMethods, calledMethods);
+
+            expectedMethods.clear();
+            calledMethods.clear();
+        }
+
+        Thread.sleep(10000); // 10 seconds
+
+
+        o = pool.borrowObject(key);
+        expectedMethods.add("borrowObject");
+        pool.returnObject(key, o);
+        expectedMethods.add("getNumIdle");
+        expectedMethods.add("invalidateObject");
+        assertEquals(expectedMethods, calledMethods);
+    }
+    
+    public void testErodingPerKeyKeyedObjectPool() throws Exception {
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1, true);
+            fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 0, true);
+            fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)null, 1f, true);
+            fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool.");
+        } catch(IllegalArgumentException iae) {
+            // expected
+        }
+
+        final List<String> calledMethods = new ArrayList<String>();
+        final InvocationHandler handler = new MethodCallLogger(calledMethods) {
+            @Override
+            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+                Object o = super.invoke(proxy, method, args);
+                if (o instanceof Integer) {
+                    // so getNumActive/getNumIdle are not zero.
+                    o = new Integer(1);
+                }
+                return o;
+            }
+        };
+
+        // If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
+        float factor = 0.01f; // about ~9 seconds until first discard
+        @SuppressWarnings("unchecked")
+        final KeyedObjectPool<Object, Object> pool = PoolUtils.erodingPool((KeyedObjectPool<Object, Object>)createProxy(KeyedObjectPool.class, handler), factor, true);
+
+        final List<String> expectedMethods = new ArrayList<String>();
+        assertEquals(expectedMethods, calledMethods);
+
+        final Object key = "key";
+
+        Object o = pool.borrowObject(key);
+        expectedMethods.add("borrowObject");
+
+        assertEquals(expectedMethods, calledMethods);
+
+        pool.returnObject(key, o);
+        expectedMethods.add("returnObject");
+        assertEquals(expectedMethods, calledMethods);
+
+        for (int i=0; i < 5; i ++) {
+            o = pool.borrowObject(key);
+            expectedMethods.add("borrowObject");
+
+            Thread.sleep(50);
+
+            pool.returnObject(key, o);
+            expectedMethods.add("returnObject");
+
+            assertEquals(expectedMethods, calledMethods);
+
+            expectedMethods.clear();
+            calledMethods.clear();
+        }
+
+        Thread.sleep(10000); // 10 seconds
+
+
+        o = pool.borrowObject(key);
+        expectedMethods.add("borrowObject");
+        pool.returnObject(key, o);
+        expectedMethods.add("getNumIdle");
+        expectedMethods.add("invalidateObject");
+        assertEquals(expectedMethods, calledMethods);
+    }
+
+    private static List<String> invokeEveryMethod(ObjectPool<Object> op) throws Exception {
+        op.addObject();
+        op.borrowObject();
+        op.clear();
+        op.close();
+        op.getNumActive();
+        op.getNumIdle();
+        op.invalidateObject(new Object());
+        op.returnObject(new Object());
+        @SuppressWarnings("unchecked")
+        final PoolableObjectFactory<Object> proxy = createProxy(PoolableObjectFactory.class, (List<String>)null);
+        op.setFactory(proxy);
+        op.toString();
+
+        final List<String> expectedMethods = Arrays.asList(new String[] {
+                "addObject", "borrowObject", "clear", "close",
+                "getNumActive", "getNumIdle", "invalidateObject",
+                "returnObject", "setFactory", "toString"
+        });
+        return expectedMethods;
+    }
+
+    private static List<String> invokeEveryMethod(KeyedObjectPool<Object, Object> kop) throws Exception {
+        kop.addObject(null);
+        kop.borrowObject(null);
+        kop.clear();
+        kop.clear(null);
+        kop.close();
+        kop.getNumActive();
+        kop.getNumActive(null);
+        kop.getNumIdle();
+        kop.getNumIdle(null);
+        kop.invalidateObject(null, new Object());
+        kop.returnObject(null, new Object());
+        @SuppressWarnings("unchecked")
+        final KeyedPoolableObjectFactory<Object, Object> proxy = createProxy(KeyedPoolableObjectFactory.class, (List<String>)null);
+        kop.setFactory(proxy);
+        kop.toString();
+
+        final List<String> expectedMethods = Arrays.asList(new String[] {
+                "addObject", "borrowObject", "clear", "clear", "close",
+                "getNumActive", "getNumActive", "getNumIdle", "getNumIdle", "invalidateObject",
+                "returnObject", "setFactory", "toString"
+        });
+        return expectedMethods;
+    }
+
+    private static List<String> invokeEveryMethod(PoolableObjectFactory<?> pof) throws Exception {
+        pof.activateObject(null);
+        pof.destroyObject(null);
+        pof.makeObject();
+        pof.passivateObject(null);
+        pof.validateObject(null);
+        pof.toString();
+
+        final List<String> expectedMethods = Arrays.asList(new String[] {
+                "activateObject", "destroyObject", "makeObject",
+                "passivateObject", "validateObject", "toString",
+        });
+        return expectedMethods;
+    }
+
+    private static List<String> invokeEveryMethod(KeyedPoolableObjectFactory<?, ?> kpof) throws Exception {
+        kpof.activateObject(null, null);
+        kpof.destroyObject(null, null);
+        kpof.makeObject(null);
+        kpof.passivateObject(null, null);
+        kpof.validateObject(null, null);
+        kpof.toString();
+
+        final List<String> expectedMethods = Arrays.asList(new String[] {
+                "activateObject", "destroyObject", "makeObject",
+                "passivateObject", "validateObject", "toString",
+        });
+        return expectedMethods;
+    }
+
+    /**
+     * Call sites can use <code>@SuppressWarnings("unchecked")</code>
+     */
+    private static <T> T createProxy(final Class<T> clazz, final List<String> logger) {
+        return createProxy(clazz, new MethodCallLogger(logger));
+    }
+
+    /**
+     * Call sites can use <code>@SuppressWarnings("unchecked")</code>
+     */
+    private static <T> T createProxy(final Class<T> clazz, final InvocationHandler handler) {        
+        return clazz.cast(Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, handler));
+    }
+
+    private static class MethodCallLogger implements InvocationHandler {
+        private final List<String> calledMethods;
+
+        MethodCallLogger(final List<String> calledMethods) {
+            this.calledMethods = calledMethods;
+        }
+
+        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+            calledMethods.add(method.getName());
+            if (boolean.class.equals(method.getReturnType())) {
+                return Boolean.FALSE;
+            } else if (int.class.equals(method.getReturnType())) {
+                return new Integer(0);
+            } else if (long.class.equals(method.getReturnType())) {
+                return new Long(0);
+            } else if (Object.class.equals(method.getReturnType())) {
+                return new Object();
+            } else {
+                return null;
+            }
+        }
+    }
+}
diff --git a/src/test/org/apache/commons/pool/VisitTracker.java b/src/test/org/apache/commons/pool/VisitTracker.java
new file mode 100644
index 0000000..8d5a944
--- /dev/null
+++ b/src/test/org/apache/commons/pool/VisitTracker.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool;
+
+/**
+ * Test pooled object class.  Keeps track of how many times it has been
+ * validated, activated, passivated.
+ *
+ */
+public class VisitTracker<K> {
+    private int validateCount = 0;
+    private int activateCount = 0;
+    private int passivateCount = 0;
+    private boolean destroyed = false;
+    private int id = 0;
+    private K key = null;
+    
+    public VisitTracker() {
+        super();
+        reset();
+    }
+    
+    public VisitTracker(int id) {
+        super();
+        this.id = id;
+        reset();
+    }
+    
+    public VisitTracker(int id, K key) {
+        super();
+        this.id = id;
+        this.key = key;
+        reset();
+    }
+    
+    public boolean validate() {
+        if (destroyed) {
+            fail("attempted to validate a destroyed object");
+        }
+        validateCount++;
+        return true;
+    }
+    public void activate() {
+        if (destroyed) {
+            fail("attempted to activate a destroyed object");
+        }
+        activateCount++;
+    }
+    public void passivate() {
+        if (destroyed) {
+            fail("attempted to passivate a destroyed object");
+        }
+        passivateCount++;
+    }
+    public void reset() {
+        validateCount = 0;
+        activateCount = 0;
+        passivateCount = 0;
+        destroyed = false;
+    }
+    public void destroy() {
+        destroyed = true;
+    }
+    public int getValidateCount() {
+        return validateCount;
+    }
+    public int getActivateCount() {
+        return activateCount;
+    }
+    public int getPassivateCount() {
+        return passivateCount;
+    }
+    public boolean isDestroyed() {
+        return destroyed;
+    }
+    public int getId() {
+        return id;
+    }
+    public K getKey() {
+        return key;
+    }
+    @Override
+    public String toString() {
+        return "Key: " + key + " id: " + id;
+    }
+    
+    private void fail(String message) {
+        throw new IllegalStateException(message);
+    }
+}
diff --git a/src/test/org/apache/commons/pool/VisitTrackerFactory.java b/src/test/org/apache/commons/pool/VisitTrackerFactory.java
new file mode 100644
index 0000000..f5c1dfc
--- /dev/null
+++ b/src/test/org/apache/commons/pool/VisitTrackerFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * Factory that creates VisitTracker instances. Used to
+ * test Evictor runs.
+ *
+ */
+public class VisitTrackerFactory<K> implements PoolableObjectFactory<VisitTracker<K>>, 
+    KeyedPoolableObjectFactory<K, VisitTracker<K>> {
+    private int nextId = 0;
+    public VisitTrackerFactory() {
+        super();
+    }
+    public VisitTracker<K> makeObject() {
+        return new VisitTracker<K>(nextId++);
+    }
+    public VisitTracker<K> makeObject(K key) {
+        return new VisitTracker<K>(nextId++, key);
+    }
+    public void destroyObject(VisitTracker<K> obj) {
+        obj.destroy();
+    }
+    public void destroyObject(K key, VisitTracker<K> obj) {
+        obj.destroy();
+    }
+    public boolean validateObject(VisitTracker<K> obj) {
+        return obj.validate();
+    }
+    public boolean validateObject(K key, VisitTracker<K> obj) {
+        return obj.validate();
+    }
+    public void activateObject(VisitTracker<K> obj) throws Exception {
+        obj.activate();
+    }
+    public void activateObject(K key, VisitTracker<K> obj) throws Exception {
+        obj.activate();
+    }
+    public void passivateObject(VisitTracker<K> obj) throws Exception {
+        obj.passivate();
+    }
+    public void passivateObject(K key, VisitTracker<K> obj) throws Exception {
+        obj.passivate();
+    }
+    public void resetId() {
+        nextId = 0;
+    }
+}
diff --git a/src/test/org/apache/commons/pool/Waiter.java b/src/test/org/apache/commons/pool/Waiter.java
new file mode 100644
index 0000000..1316737
--- /dev/null
+++ b/src/test/org/apache/commons/pool/Waiter.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+/**
+ * <p>Object created by {@link WaiterFactory}. Maintains active / valid state,
+ * last passivated and idle times.  Waits with configurable latency when 
+ * {@link #doWait()} method is called.</p>
+ *
+ * <p>This class is *not* threadsafe.</p>
+ */
+public class Waiter {
+    private boolean active = false;
+    private boolean valid = true;
+    private long latency = 0;
+    private long lastPassivated = 0;
+    private long lastIdleTimeMs = 0;
+    
+    public Waiter(boolean active, boolean valid, long latency) {
+        this.active = active;
+        this.valid = valid;
+        this.latency = latency;
+        this.lastPassivated = System.currentTimeMillis();
+    }
+
+    /**
+     * Wait for {@link #getLatency()} ms.
+     */
+    public void doWait() {
+        try {
+            Thread.sleep(latency);
+        } catch (InterruptedException ex) {
+            // ignore
+        }
+    }
+
+    /**
+     * Whether or not the instance is active.
+     * 
+     * @return true if the last lifecycle event for this instance was activation.
+     */
+    public boolean isActive() {
+        return active;
+    }
+
+    /**
+     * <p>Sets the active state and updates {@link #getLastIdleTimeMs() lastIdleTime}
+     * or {@link #getLastPassivated() lastPassivated} as appropriate.</p>
+     * 
+     * <p>If the active state is changing from inactive to active, lastIdleTime
+     * is updated with the current time minus lastPassivated.  If the state is
+     * changing from active to inactive, lastPassivated is updated with the
+     * current time.</p>
+     * 
+     * <p>{@link WaiterFactory#activateObject(Waiter)} and
+     * {@link WaiterFactory#passivateObject(Waiter)} invoke this method on their
+     * actual parameter, passing <code>true</code> and <code>false</code>,
+     * respectively.</p>
+     * 
+     * @param active new active state
+     */
+    public void setActive(boolean active) {
+        final boolean activeState = this.active;
+        if (activeState == active) {
+            return;
+        }
+        this.active = active;
+        final long currentTime = System.currentTimeMillis();
+        if (active) {  // activating
+            lastIdleTimeMs = currentTime - lastPassivated;
+        } else {       // passivating
+            lastPassivated = currentTime;
+        }
+    }
+
+    public long getLatency() {
+        return latency;
+    }
+
+    public void setLatency(long latency) {
+        this.latency = latency;
+    }
+
+    public boolean isValid() {
+        return valid;
+    }
+
+    public void setValid(boolean valid) {
+        this.valid = valid;
+    }
+    
+    /**
+     * <p>Returns the system time of this instance's last passivation.</p>
+     * 
+     * <p>When an instance is created, this field is initialized to the system time.</p>
+     * 
+     * @return time of last passivation
+     */
+    public long getLastPassivated() {
+        return lastPassivated;
+    }
+    
+    /**
+     * <p>Returns the last idle time for this instance in ms.</p>
+     * 
+     * <p>When an instance is created, and each subsequent time it is passivated,
+     * the {@link #getLastPassivated() lastPassivated} property is updated with the
+     * current time.  When the next activation occurs, <code>lastIdleTime</code> is
+     * updated with the elapsed time since passivation.<p>
+     * 
+     * @return last idle time
+     */
+    public long getLastIdleTimeMs() {
+        return lastIdleTimeMs;
+    }
+    
+}
diff --git a/src/test/org/apache/commons/pool/WaiterFactory.java b/src/test/org/apache/commons/pool/WaiterFactory.java
new file mode 100644
index 0000000..f9d42c9
--- /dev/null
+++ b/src/test/org/apache/commons/pool/WaiterFactory.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Object factory with configurable latencies for object lifecycle methods.
+ * This factory will also track and enforce maxActive, maxActivePerKey contracts.
+ * If the factory's maxActive / maxActivePerKey are set to match those of the
+ * pool, makeObject will throw IllegalStateException if the number of makes - destroys
+ * (per key) exceeds the configured max.
+ *
+ */
+public class WaiterFactory<K> implements PoolableObjectFactory<Waiter>,
+KeyedPoolableObjectFactory<K, Waiter> {
+    
+    /** Latency of activateObject */
+    private final long activateLatency;
+    
+    /** Latency of destroyObject */
+    private final long destroyLatency;
+    
+    /** Latency of makeObject */
+    private final long makeLatency;
+    
+    /** Latency of passivateObject */
+    private final long passivateLatency;
+    
+    /** Latency of validateObject */
+    private final long validateLatency;
+    
+    /** Latency of doWait for Waiter instances created by this factory */
+    private final long waiterLatency;
+    
+    /** Probability that passivation will invalidate Waiter instances */
+    private final double passivateInvalidationProbability;
+    
+    /** Count of (makes - destroys) since last reset */
+    private long activeCount = 0;
+    
+    /** Count of (makes - destroys) per key since last reset */
+    private Map<K, Integer> activeCounts = new HashMap<K, Integer>();
+    
+    /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
+    private final long maxActive;  // GKOP 1.x calls this maxTotal
+    
+    /** Maximum of (makes - destroys) per key */
+    private final long maxActivePerKey;  // GKOP 1.x calls this maxActive
+
+    public WaiterFactory(long activateLatency, long destroyLatency,
+            long makeLatency, long passivateLatency, long validateLatency,
+            long waiterLatency,long maxActive, long maxActivePerKey,
+            double passivateInvalidationProbability) {
+        this.activateLatency = activateLatency;
+        this.destroyLatency = destroyLatency;
+        this.makeLatency = makeLatency;
+        this.passivateLatency = passivateLatency;
+        this.validateLatency = validateLatency;
+        this.waiterLatency = waiterLatency;
+        this.maxActive = maxActive;
+        this.maxActivePerKey = maxActivePerKey;
+        this.passivateInvalidationProbability = passivateInvalidationProbability;
+    }
+    
+    public WaiterFactory(long activateLatency, long destroyLatency,
+            long makeLatency, long passivateLatency, long validateLatency,
+            long waiterLatency) {
+        this(activateLatency, destroyLatency, makeLatency, passivateLatency,
+                validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
+    }
+    
+    public WaiterFactory(long activateLatency, long destroyLatency,
+            long makeLatency, long passivateLatency, long validateLatency,
+            long waiterLatency,long maxActive) {
+        this(activateLatency, destroyLatency, makeLatency, passivateLatency,
+                validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
+    }
+
+    public void activateObject(Waiter obj) throws Exception {
+        doWait(activateLatency);
+        obj.setActive(true);
+    }
+
+    public void destroyObject(Waiter obj) throws Exception {
+        doWait(destroyLatency);
+        obj.setValid(false);
+        obj.setActive(false);
+        // Decrement *after* destroy 
+        synchronized (this) {
+            activeCount--;
+        }
+    }
+
+    public Waiter makeObject() throws Exception {
+        // Increment and test *before* make
+        synchronized (this) {
+            if (activeCount >= maxActive) {
+                throw new IllegalStateException("Too many active instances: " +
+                activeCount + " in circulation with maxActive = " + maxActive);
+            } else {
+                activeCount++;
+            }
+        }
+        doWait(makeLatency);
+        return new Waiter(false, true, waiterLatency);
+    }
+
+    public void passivateObject(Waiter arg0) throws Exception {
+        arg0.setActive(false);
+        doWait(passivateLatency);
+        if (Math.random() < passivateInvalidationProbability) {
+            arg0.setValid(false);
+        }
+    }
+
+    public boolean validateObject(Waiter arg0) {
+        doWait(validateLatency);
+        return arg0.isValid();
+    }
+    
+    protected void doWait(long latency) {
+        try {
+            Thread.sleep(latency);
+        } catch (InterruptedException ex) {
+            // ignore
+        }
+    }
+    
+    public synchronized void reset() {
+        activeCount = 0;
+        if (activeCounts.isEmpty()) {
+            return;
+        }
+        Iterator<K> it = activeCounts.keySet().iterator();
+        while (it.hasNext()) {
+            K key = it.next();
+            activeCounts.put(key, new Integer (0));
+        }
+    }
+
+    /**
+     * @return the maxActive
+     */
+    public synchronized long getMaxActive() {
+        return maxActive;
+    }
+
+    // KeyedPoolableObjectFactory methods
+    
+    public void activateObject(K key, Waiter obj) throws Exception {
+        activateObject(obj);
+    }
+
+    public void destroyObject(K key, Waiter obj) throws Exception {
+        destroyObject(obj);
+        synchronized (this) {
+            Integer count = activeCounts.get(key);
+            activeCounts.put(key, new Integer(count.intValue() - 1));
+        }
+    }
+
+    public Waiter makeObject(K key) throws Exception {
+        synchronized (this) {
+            Integer count = activeCounts.get(key);
+            if (count == null) {
+                count = new Integer(1);
+                activeCounts.put(key, count);
+            } else {
+                if (count.intValue() >= maxActivePerKey) {
+                    throw new IllegalStateException("Too many active " +
+                    "instances for key = " + key + ": " + count.intValue() + 
+                    " in circulation " + "with maxActivePerKey = " + 
+                    maxActivePerKey);
+                } else {
+                    activeCounts.put(key, new Integer(count.intValue() + 1));
+                }
+            }
+        }
+        return makeObject();
+    }
+
+    public void passivateObject(K key, Waiter obj) throws Exception {
+        passivateObject(obj);
+    }
+
+    public boolean validateObject(K key, Waiter obj) {
+        return validateObject(obj);
+    }
+
+}
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..2b257e9
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPool.java
@@ -0,0 +1,1756 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+import org.apache.commons.pool.TestBaseKeyedObjectPool;
+import org.apache.commons.pool.VisitTracker;
+import org.apache.commons.pool.VisitTrackerFactory;
+import org.apache.commons.pool.Waiter;
+import org.apache.commons.pool.WaiterFactory;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestGenericKeyedObjectPool extends TestBaseKeyedObjectPool<Object, String> {
+    public TestGenericKeyedObjectPool(String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected KeyedObjectPool<Object, String> makeEmptyPool(int mincapacity) {
+        GenericKeyedObjectPool<Object, String> pool = new GenericKeyedObjectPool<Object, String>(
+            new KeyedPoolableObjectFactory<Object, String>()  {
+                HashMap<Object, Integer> map = new HashMap<Object, Integer>();
+                public String makeObject(Object key) {
+                    int counter = 0;
+                    Integer Counter = map.get(key);
+                    if(null != Counter) {
+                        counter = Counter.intValue();
+                    }
+                    map.put(key,new Integer(counter + 1));
+                    return String.valueOf(key) + String.valueOf(counter);
+                }
+                public void destroyObject(Object key, String obj) { }
+                public boolean validateObject(Object key, String obj) { return true; }
+                public void activateObject(Object key, String obj) { }
+                public void passivateObject(Object key, String obj) { }
+            }
+        );
+        pool.setMaxActive(mincapacity);
+        pool.setMaxIdle(mincapacity);
+        return pool;
+    }
+
+    @Override
+    protected KeyedObjectPool<Object, Integer> makeEmptyPool(KeyedPoolableObjectFactory<Object, Integer> factory) {
+        return new GenericKeyedObjectPool<Object, Integer>(factory);
+    }
+
+    @Override
+    protected String getNthObject(Object key, int n) {
+        return String.valueOf(key) + String.valueOf(n);
+    }
+
+    @Override
+    protected Object makeKey(int n) {
+        return String.valueOf(n);
+    }
+
+    private GenericKeyedObjectPool<String, String> pool = null;
+    private final Integer zero = new Integer(0);
+    private final Integer one = new Integer(1);
+    private final Integer two = new Integer(2);
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        pool = new GenericKeyedObjectPool<String, String>(new SimpleFactory<String>());
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        pool.clear();
+        pool.close();
+        pool = null;
+    }
+
+    public void testNegativeMaxActive() throws Exception {
+        pool.setMaxActive(-1);
+        pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
+        String obj = pool.borrowObject("");
+        assertEquals("0",obj);
+        pool.returnObject("",obj);
+    }
+
+    public void testNumActiveNumIdle2() throws Exception {
+        assertEquals(0,pool.getNumActive());
+        assertEquals(0,pool.getNumIdle());
+        assertEquals(0,pool.getNumActive("A"));
+        assertEquals(0,pool.getNumIdle("A"));
+        assertEquals(0,pool.getNumActive("B"));
+        assertEquals(0,pool.getNumIdle("B"));
+
+        String objA0 = pool.borrowObject("A");
+        String objB0 = pool.borrowObject("B");
+
+        assertEquals(2,pool.getNumActive());
+        assertEquals(0,pool.getNumIdle());
+        assertEquals(1,pool.getNumActive("A"));
+        assertEquals(0,pool.getNumIdle("A"));
+        assertEquals(1,pool.getNumActive("B"));
+        assertEquals(0,pool.getNumIdle("B"));
+
+        String objA1 = pool.borrowObject("A");
+        String objB1 = pool.borrowObject("B");
+
+        assertEquals(4,pool.getNumActive());
+        assertEquals(0,pool.getNumIdle());
+        assertEquals(2,pool.getNumActive("A"));
+        assertEquals(0,pool.getNumIdle("A"));
+        assertEquals(2,pool.getNumActive("B"));
+        assertEquals(0,pool.getNumIdle("B"));
+
+        pool.returnObject("A",objA0);
+        pool.returnObject("B",objB0);
+
+        assertEquals(2,pool.getNumActive());
+        assertEquals(2,pool.getNumIdle());
+        assertEquals(1,pool.getNumActive("A"));
+        assertEquals(1,pool.getNumIdle("A"));
+        assertEquals(1,pool.getNumActive("B"));
+        assertEquals(1,pool.getNumIdle("B"));
+
+        pool.returnObject("A",objA1);
+        pool.returnObject("B",objB1);
+
+        assertEquals(0,pool.getNumActive());
+        assertEquals(4,pool.getNumIdle());
+        assertEquals(0,pool.getNumActive("A"));
+        assertEquals(2,pool.getNumIdle("A"));
+        assertEquals(0,pool.getNumActive("B"));
+        assertEquals(2,pool.getNumIdle("B"));
+    }
+
+    public void testMaxIdle() throws Exception {
+        pool.setMaxActive(100);
+        pool.setMaxIdle(8);
+        String[] active = new String[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        assertEquals(100,pool.getNumActive(""));
+        assertEquals(0,pool.getNumIdle(""));
+        for(int i=0;i<100;i++) {
+            pool.returnObject("",active[i]);
+            assertEquals(99 - i,pool.getNumActive(""));
+            assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle(""));
+        }
+        
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject("a");
+        }
+        assertEquals(100,pool.getNumActive("a"));
+        assertEquals(0,pool.getNumIdle("a"));
+        for(int i=0;i<100;i++) {
+            pool.returnObject("a",active[i]);
+            assertEquals(99 - i,pool.getNumActive("a"));
+            assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle("a"));
+        }
+        
+        // total number of idle instances is twice maxIdle
+        assertEquals(16, pool.getNumIdle());
+        // Each pool is at the sup
+        assertEquals(8, pool.getNumIdle(""));
+        assertEquals(8, pool.getNumIdle("a"));
+             
+    }
+
+    public void testMaxActive() throws Exception {
+        pool.setMaxActive(3);
+        pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
+
+        pool.borrowObject("");
+        pool.borrowObject("");
+        pool.borrowObject("");
+        try {
+            pool.borrowObject("");
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    public void testMaxActiveZero() throws Exception {
+        pool.setMaxActive(0);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+
+        try {
+            pool.borrowObject("a");
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+    
+    public void testWhenExhaustedGrow() throws Exception {
+        pool.setMaxActive(1);
+        pool.setMaxTotal(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
+        for (int i = 0; i < 10; i++) {
+            pool.borrowObject("a");
+        }
+    }
+
+    public void testWhenExhaustedBlockClosePool() throws Exception {
+        pool.setMaxActive(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(0);
+        Object obj1 = pool.borrowObject("a");
+        
+        // Make sure an object was obtained
+        assertNotNull(obj1);
+        
+        // Create a separate thread to try and borrow another object
+        WaitingTestThread<String, String> wtt = new WaitingTestThread<String, String>(pool, "a", 200);
+        wtt.start();
+        // Give wtt time to start
+        Thread.sleep(200);
+        
+        // close the pool (Bug POOL-189)
+        pool.close();
+        
+        // Give interrupt time to take effect
+        Thread.sleep(200);
+        
+        // Check thread was interrupted
+        assertTrue(wtt._thrown instanceof IllegalStateException);
+    }
+
+    public void testMaxTotal() throws Exception {
+        pool.setMaxActive(2);
+        pool.setMaxTotal(3);
+        pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
+
+        String o1 = pool.borrowObject("a");
+        assertNotNull(o1);
+        String o2 = pool.borrowObject("a");
+        assertNotNull(o2);
+        String o3 = pool.borrowObject("b");
+        assertNotNull(o3);
+        try {
+            pool.borrowObject("c");
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+
+        assertEquals(0, pool.getNumIdle());
+
+        pool.returnObject("b", o3);
+        assertEquals(1, pool.getNumIdle());
+        assertEquals(1, pool.getNumIdle("b"));
+
+        Object o4 = pool.borrowObject("b");
+        assertNotNull(o4);
+        assertEquals(0, pool.getNumIdle());
+        assertEquals(0, pool.getNumIdle("b"));
+        
+        pool.setMaxTotal(4);
+        Object o5 = pool.borrowObject("b");
+        assertNotNull(o5);
+        
+        assertEquals(2, pool.getNumActive("a"));
+        assertEquals(2, pool.getNumActive("b"));
+        assertEquals(pool.getMaxTotal(),
+                pool.getNumActive("b") + pool.getNumActive("b"));
+        assertEquals(pool.getNumActive(),
+                pool.getMaxTotal());
+    }
+
+    public void testMaxTotalZero() throws Exception {
+        pool.setMaxTotal(0);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+
+        try {
+            pool.borrowObject("a");
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    public void testMaxTotalLRU() throws Exception {
+        pool.setMaxActive(2);
+        pool.setMaxTotal(3);
+//        pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW);
+
+        String o1 = pool.borrowObject("a");
+        assertNotNull(o1);
+        pool.returnObject("a", o1);
+        Thread.sleep(25);
+
+        String o2 = pool.borrowObject("b");
+        assertNotNull(o2);
+        pool.returnObject("b", o2);
+        Thread.sleep(25);
+
+        String o3 = pool.borrowObject("c");
+        assertNotNull(o3);
+        pool.returnObject("c", o3);
+        Thread.sleep(25);
+
+        String o4 = pool.borrowObject("a");
+        assertNotNull(o4);
+        pool.returnObject("a", o4);
+        Thread.sleep(25);
+
+        assertSame(o1, o4);
+
+        // this should cause b to be bumped out of the pool
+        String o5 = pool.borrowObject("d");
+        assertNotNull(o5);
+        pool.returnObject("d", o5);
+        Thread.sleep(25);
+
+        // now re-request b, we should get a different object because it should
+        // have been expelled from pool (was oldest because a was requested after b)
+        String o6 = pool.borrowObject("b");
+        assertNotNull(o6);
+        pool.returnObject("b", o6);
+
+        assertNotSame(o1, o6);
+
+        // second a is still in there
+        String o7 = pool.borrowObject("a");
+        assertNotNull(o7);
+        pool.returnObject("a", o7);
+
+        assertSame(o4, o7);
+    }
+
+    public void testSettersAndGetters() throws Exception {
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>();
+        {
+            pool.setFactory(new SimpleFactory<String>());
+        }
+        {
+            pool.setMaxActive(123);
+            assertEquals(123,pool.getMaxActive());
+        }
+        {
+            pool.setMaxIdle(12);
+            assertEquals(12,pool.getMaxIdle());
+        }
+        {
+            pool.setMaxWait(1234L);
+            assertEquals(1234L,pool.getMaxWait());
+        }
+        {
+            pool.setMinEvictableIdleTimeMillis(12345L);
+            assertEquals(12345L,pool.getMinEvictableIdleTimeMillis());
+        }
+        {
+            pool.setNumTestsPerEvictionRun(11);
+            assertEquals(11,pool.getNumTestsPerEvictionRun());
+        }
+        {
+            pool.setTestOnBorrow(true);
+            assertTrue(pool.getTestOnBorrow());
+            pool.setTestOnBorrow(false);
+            assertTrue(!pool.getTestOnBorrow());
+        }
+        {
+            pool.setTestOnReturn(true);
+            assertTrue(pool.getTestOnReturn());
+            pool.setTestOnReturn(false);
+            assertTrue(!pool.getTestOnReturn());
+        }
+        {
+            pool.setTestWhileIdle(true);
+            assertTrue(pool.getTestWhileIdle());
+            pool.setTestWhileIdle(false);
+            assertTrue(!pool.getTestWhileIdle());
+        }
+        {
+            pool.setTimeBetweenEvictionRunsMillis(11235L);
+            assertEquals(11235L,pool.getTimeBetweenEvictionRunsMillis());
+        }
+        {
+            pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK);
+            assertEquals(GenericObjectPool.WHEN_EXHAUSTED_BLOCK,pool.getWhenExhaustedAction());
+            pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL);
+            assertEquals(GenericObjectPool.WHEN_EXHAUSTED_FAIL,pool.getWhenExhaustedAction());
+            pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW);
+            assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW,pool.getWhenExhaustedAction());
+        }
+    }
+
+    public void testEviction() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMaxActive(500);
+        pool.setNumTestsPerEvictionRun(100);
+        pool.setMinEvictableIdleTimeMillis(250L);
+        pool.setTimeBetweenEvictionRunsMillis(500L);
+
+        String[] active = new String[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.sleep(1000L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 500);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 400);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 300);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 200);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 100);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertEquals("Should be zero idle, found " + pool.getNumIdle(""),0,pool.getNumIdle(""));
+
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject("",active[i]);
+        }
+
+        try { Thread.sleep(1000L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 500);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 400);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 300);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 200);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 100);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertEquals("Should be zero idle, found " + pool.getNumIdle(""),0,pool.getNumIdle(""));
+    }
+
+    public void testEviction2() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMaxActive(500);
+        pool.setNumTestsPerEvictionRun(100);
+        pool.setMinEvictableIdleTimeMillis(500L);
+        pool.setTimeBetweenEvictionRunsMillis(500L);
+
+        String[] active = new String[500];
+        String[] active2 = new String[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.sleep(1100L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 1000 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 1000);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 900 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 900);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 800 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 800);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 700 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 700);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 600 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 600);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
+    }
+
+    /**
+     * Kicks off <numThreads> test threads, each of which will go through
+     * <iterations> borrow-return cycles with random delay times <= delay
+     * in between.
+     */
+    public <V> void runTestThreads(GenericKeyedObjectPool<String, V> pool, int numThreads, int iterations, int delay) {
+        @SuppressWarnings("unchecked")
+        TestThread<V>[] threads = new TestThread[numThreads];
+        for(int i=0;i<numThreads;i++) {
+            threads[i] = new TestThread<V>(pool,iterations,delay);
+            Thread t = new Thread(threads[i]);
+            t.start();
+        }
+        for(int i=0;i<numThreads;i++) {
+            while(!(threads[i]).complete()) {
+                try {
+                    Thread.sleep(500L);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+            }
+            if(threads[i].failed()) {
+                fail("Thread failed: "+i+"\n"+getExceptionTrace(threads[i]._exception));
+            }
+        }
+    }
+    
+    public void testThreaded1() throws Exception {
+        pool.setMaxActive(15);
+        pool.setMaxIdle(15);
+        pool.setMaxWait(1000L);
+        runTestThreads(pool, 20, 100, 50);
+    }
+    
+    /**
+     * Verifies that maxTotal is not exceeded when factory destroyObject
+     * has high latency, testOnReturn is set and there is high incidence of
+     * validation failures. 
+     */
+    public void testMaxTotalInvariant() throws Exception {
+        int maxTotal = 15;
+        SimpleFactory<String> factory = new SimpleFactory<String>();
+        factory.setEvenValid(false);     // Every other validation fails
+        factory.setDestroyLatency(100);  // Destroy takes 100 ms
+        factory.setMaxActive(maxTotal);  // (makes - destroys) bound
+        factory.setValidationEnabled(true);
+        pool = new GenericKeyedObjectPool<String, String>(factory);
+        pool.setMaxTotal(maxTotal);
+        pool.setMaxIdle(-1);
+        pool.setTestOnReturn(true);
+        pool.setMaxWait(10000L);
+        runTestThreads(pool, 5, 10, 50);
+    }
+
+    public void testMinIdle() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMinIdle(5);
+        pool.setMaxActive(10);
+        pool.setNumTestsPerEvictionRun(0);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(100L);
+        pool.setTestWhileIdle(true);
+
+
+        //Generate a random key
+        String key = "A";
+
+        pool.preparePool(key, true);
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        String[] active = new String[5];
+        active[0] = pool.borrowObject(key);
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=1 ; i<5 ; i++) {
+            active[i] = pool.borrowObject(key);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=0 ; i<5 ; i++) {
+            pool.returnObject(key, active[i]);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
+    }
+
+    public void testMinIdleMaxActive() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMinIdle(5);
+        pool.setMaxActive(10);
+        pool.setNumTestsPerEvictionRun(0);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(100L);
+        pool.setTestWhileIdle(true);
+
+        String key = "A";
+
+        pool.preparePool(key, true);
+        assertTrue("Should be 5 idle, found " + 
+                pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        String[] active = new String[10];
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=0 ; i<5 ; i++) {
+            active[i] = pool.borrowObject(key);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=0 ; i<5 ; i++) {
+            pool.returnObject(key, active[i]);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
+
+        for(int i=0 ; i<10 ; i++) {
+            active[i] = pool.borrowObject(key);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
+
+        for(int i=0 ; i<10 ; i++) {
+            pool.returnObject(key, active[i]);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
+    }
+
+    public void testMinIdleNoPopulateImmediately() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMinIdle(5);
+        pool.setMaxActive(10);
+        pool.setNumTestsPerEvictionRun(0);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(1000L);
+        pool.setTestWhileIdle(true);
+
+
+        //Generate a random key
+        String key = "A";
+
+        pool.preparePool(key, false);
+
+        assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
+
+        try { Thread.sleep(1500L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+    }
+
+    public void testMinIdleNoPreparePool() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMinIdle(5);
+        pool.setMaxActive(10);
+        pool.setNumTestsPerEvictionRun(0);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(100L);
+        pool.setTestWhileIdle(true);
+
+
+        //Generate a random key
+        String key = "A";
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
+
+        Object active = pool.borrowObject(key);
+        assertNotNull(active);
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+    }
+
+    public void testFIFO() throws Exception {
+        pool.setLifo(false);
+        final String key = "key";
+        pool.addObject(key); // "key0"
+        pool.addObject(key); // "key1"
+        pool.addObject(key); // "key2"
+        assertEquals("Oldest", "key0", pool.borrowObject(key));
+        assertEquals("Middle", "key1", pool.borrowObject(key));
+        assertEquals("Youngest", "key2", pool.borrowObject(key));
+        assertEquals("new-3", "key3", pool.borrowObject(key));
+        pool.returnObject(key, "r");
+        assertEquals("returned", "r", pool.borrowObject(key));
+        assertEquals("new-4", "key4", pool.borrowObject(key));
+    }
+    
+    public void testLIFO() throws Exception {
+        pool.setLifo(true);
+        final String key = "key";
+        pool.addObject(key); // "key0"
+        pool.addObject(key); // "key1"
+        pool.addObject(key); // "key2"
+        assertEquals("Youngest", "key2", pool.borrowObject(key));
+        assertEquals("Middle", "key1", pool.borrowObject(key));
+        assertEquals("Oldest", "key0", pool.borrowObject(key));
+        assertEquals("new-3", "key3", pool.borrowObject(key));
+        pool.returnObject(key, "r");
+        assertEquals("returned", "r", pool.borrowObject(key));
+        assertEquals("new-4", "key4", pool.borrowObject(key));
+    }
+    
+    /**
+     * Test to make sure evictor visits least recently used objects first,
+     * regardless of FIFO/LIFO 
+     * 
+     * JIRA: POOL-86
+     */ 
+    public void testEvictionOrder() throws Exception {
+        checkEvictionOrder(false);
+        checkEvictionOrder(true);
+    }
+    
+    private void checkEvictionOrder(boolean lifo) throws Exception {
+        SimpleFactory<Integer> factory = new SimpleFactory<Integer>();
+        GenericKeyedObjectPool<Integer, String> pool = new GenericKeyedObjectPool<Integer, String>(factory);
+        pool.setNumTestsPerEvictionRun(2);
+        pool.setMinEvictableIdleTimeMillis(100);
+        pool.setLifo(lifo);
+        
+        for (int i = 0; i < 3; i ++) {
+            Integer key = new Integer(i);
+            for (int j = 0; j < 5; j++) {
+                pool.addObject(key);
+            }
+        }
+        
+        // Make all evictable
+        Thread.sleep(200);
+        
+        /* 
+         * Initial state (Key, Object) pairs in order of age:
+         * 
+         * (0,0), (0,1), (0,2), (0,3), (0,4)
+         * (1,5), (1,6), (1,7), (1,8), (1,9)
+         * (2,10), (2,11), (2,12), (2,13), (2,14)
+         */
+        
+        pool.evict(); // Kill (0,0),(0,1)
+        assertEquals(3, pool.getNumIdle(zero));
+        String objZeroA = pool.borrowObject(zero);
+        assertTrue(lifo ? objZeroA.equals("04") : objZeroA.equals("02"));
+        assertEquals(2, pool.getNumIdle(zero));
+        String objZeroB = pool.borrowObject(zero);
+        assertTrue(objZeroB.equals("03"));
+        assertEquals(1, pool.getNumIdle(zero));
+        
+        pool.evict(); // Kill remaining 0 survivor and (1,5)
+        assertEquals(0, pool.getNumIdle(zero));
+        assertEquals(4, pool.getNumIdle(one));
+        String objOneA = pool.borrowObject(one);
+        assertTrue(lifo ? objOneA.equals("19") : objOneA.equals("16"));
+        assertEquals(3, pool.getNumIdle(one));
+        String objOneB = pool.borrowObject(one);
+        assertTrue(lifo ? objOneB.equals("18") : objOneB.equals("17"));
+        assertEquals(2, pool.getNumIdle(one));
+        
+        pool.evict(); // Kill remaining 1 survivors
+        assertEquals(0, pool.getNumIdle(one));
+        pool.evict(); // Kill (2,10), (2,11)
+        assertEquals(3, pool.getNumIdle(two));
+        String objTwoA = pool.borrowObject(two);
+        assertTrue(lifo ? objTwoA.equals("214") : objTwoA.equals("212"));
+        assertEquals(2, pool.getNumIdle(two));
+        pool.evict(); // All dead now
+        assertEquals(0, pool.getNumIdle(two));  
+        
+        pool.evict(); // Should do nothing - make sure no exception
+        // Currently 2 zero, 2 one and 1 two active. Return them
+        pool.returnObject(zero, objZeroA);
+        pool.returnObject(zero, objZeroB);
+        pool.returnObject(one, objOneA);
+        pool.returnObject(one, objOneB);
+        pool.returnObject(two, objTwoA);
+        // Remove all idle objects
+        pool.clear();
+        
+        // Reload
+        pool.setMinEvictableIdleTimeMillis(500);
+        factory.counter = 0; // Reset counter
+        for (int i = 0; i < 3; i ++) {
+            Integer key = new Integer(i);
+            for (int j = 0; j < 5; j++) {
+                pool.addObject(key);
+            }
+            Thread.sleep(200);
+        }
+        
+        // 0's are evictable, others not 
+        pool.evict(); // Kill (0,0),(0,1)
+        assertEquals(3, pool.getNumIdle(zero));
+        pool.evict(); // Kill (0,2),(0,3)
+        assertEquals(1, pool.getNumIdle(zero));
+        pool.evict(); // Kill (0,4), leave (1,5)
+        assertEquals(0, pool.getNumIdle(zero));
+        assertEquals(5, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        pool.evict(); // (1,6), (1,7)
+        assertEquals(5, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        pool.evict(); // (1,8), (1,9)
+        assertEquals(5, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        pool.evict(); // (2,10), (2,11)
+        assertEquals(5, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        pool.evict(); // (2,12), (2,13)
+        assertEquals(5, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        pool.evict(); // (2,14), (1,5)
+        assertEquals(5, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        Thread.sleep(200); // Ones now timed out
+        pool.evict(); // kill (1,6), (1,7) - (1,5) missed
+        assertEquals(3, pool.getNumIdle(one));
+        assertEquals(5, pool.getNumIdle(two));
+        Object obj = pool.borrowObject(one);
+        if (lifo) {
+            assertEquals("19", obj);
+        } else {
+            assertEquals("15", obj);
+        }
+    }
+    
+    
+    /**
+     * Verifies that the evictor visits objects in expected order
+     * and frequency. 
+     */
+    public void testEvictorVisiting() throws Exception {
+        checkEvictorVisiting(true);
+        checkEvictorVisiting(false);  
+    }
+    
+    private void checkEvictorVisiting(boolean lifo) throws Exception {
+        VisitTrackerFactory<Integer> factory = new VisitTrackerFactory<Integer>();
+        GenericKeyedObjectPool<Integer, VisitTracker<Integer>> pool = new GenericKeyedObjectPool<Integer, VisitTracker<Integer>>(factory);
+        pool.setNumTestsPerEvictionRun(2);
+        pool.setMinEvictableIdleTimeMillis(-1);
+        pool.setTestWhileIdle(true);
+        pool.setLifo(lifo);
+        pool.setTestOnReturn(false);
+        pool.setTestOnBorrow(false);
+        for (int i = 0; i < 3; i ++) {
+            factory.resetId();
+            Integer key = new Integer(i);
+            for (int j = 0; j < 8; j++) {
+                pool.addObject(key);
+            }
+        }
+        pool.evict(); // Visit oldest 2 - 00 and 01
+        VisitTracker<Integer> obj = pool.borrowObject(zero);
+        pool.returnObject(zero, obj);
+        obj = pool.borrowObject(zero);
+        pool.returnObject(zero, obj);
+        //  borrow, return, borrow, return 
+        //  FIFO will move 0 and 1 to end - 2,3,4,5,6,7,0,1
+        //  LIFO, 7 out, then in, then out, then in - 7,6,5,4,3,2,1,0
+        pool.evict();  // Should visit 02 and 03 in either case
+        for (int i = 0; i < 8; i++) {
+            VisitTracker<Integer> tracker = pool.borrowObject(zero);    
+            if (tracker.getId() >= 4) {
+                assertEquals("Unexpected instance visited " + tracker.getId(),
+                        0, tracker.getValidateCount());
+            } else {
+                assertEquals("Instance " +  tracker.getId() + 
+                        " visited wrong number of times.",
+                        1, tracker.getValidateCount());
+            }
+        } 
+        // 0's are all out
+        
+        pool.setNumTestsPerEvictionRun(3);
+        
+        pool.evict(); // 10, 11, 12
+        pool.evict(); // 13, 14, 15
+        
+        obj = pool.borrowObject(one);
+        pool.returnObject(one, obj);
+        obj = pool.borrowObject(one);
+        pool.returnObject(one, obj);
+        obj = pool.borrowObject(one);
+        pool.returnObject(one, obj);
+        // borrow, return, borrow, return 
+        //  FIFO 3,4,5,^,6,7,0,1,2
+        //  LIFO 7,6,^,5,4,3,2,1,0
+        // In either case, pointer should be at 6
+        pool.evict();
+        // LIFO - 16, 17, 20
+        // FIFO - 16, 17, 10
+        pool.evict();
+        // LIFO - 21, 22, 23
+        // FIFO - 11, 12, 20
+        pool.evict();
+        // LIFO - 24, 25, 26
+        // FIFO - 21, 22, 23
+        pool.evict();
+        // LIFO - 27, skip, 10
+        // FIFO - 24, 25, 26
+        for (int i = 0; i < 8; i++) {
+            VisitTracker<Integer> tracker = pool.borrowObject(one);    
+            if ((lifo && tracker.getId() > 0) || 
+                    (!lifo && tracker.getId() > 2)) {
+                assertEquals("Instance " +  tracker.getId() + 
+                        " visited wrong number of times.",
+                        1, tracker.getValidateCount());
+            } else {
+                assertEquals("Instance " +  tracker.getId() + 
+                        " visited wrong number of times.",
+                        2, tracker.getValidateCount());
+            }
+        } 
+        
+        // Randomly generate some pools with random numTests
+        // and make sure evictor cycles through elements appropriately
+        int[] smallPrimes = {2, 3, 5, 7};
+        Random random = new Random();
+        random.setSeed(System.currentTimeMillis());
+        pool.setMaxIdle(-1);
+        for (int i = 0; i < smallPrimes.length; i++) {
+            pool.setNumTestsPerEvictionRun(smallPrimes[i]);
+            for (int j = 0; j < 5; j++) {// Try the tests a few times
+                pool.clear();
+                assertEquals("NumIdle should be zero after clearing the pool",0,pool.getNumIdle());
+                int zeroLength = 10 + random.nextInt(20);
+                for (int k = 0; k < zeroLength; k++) {
+                    pool.addObject(zero);
+                }
+                int oneLength = 10 + random.nextInt(20);
+                for (int k = 0; k < oneLength; k++) {
+                    pool.addObject(one);
+                }
+                int twoLength = 10 + random.nextInt(20);
+                for (int k = 0; k < twoLength; k++) {
+                    pool.addObject(two);
+                }
+                
+                // Choose a random number of evictor runs
+                int runs = 10 + random.nextInt(50);
+                for (int k = 0; k < runs; k++) {
+                    pool.evict();
+                }
+                
+                // Total instances in pool
+                int totalInstances = zeroLength + oneLength + twoLength;
+                
+                // Number of times evictor should have cycled through pools
+                int cycleCount = (runs * pool.getNumTestsPerEvictionRun())
+                    / totalInstances;
+                
+                // Look at elements and make sure they are visited cycleCount
+                // or cycleCount + 1 times
+                VisitTracker<Integer> tracker = null;
+                int visitCount = 0;
+                for (int k = 0; k < zeroLength; k++) {
+                    tracker = pool.borrowObject(zero); 
+                    visitCount = tracker.getValidateCount();
+                    if (visitCount < cycleCount || visitCount > cycleCount + 1){
+                        fail(formatSettings("ZERO", "runs", runs, "lifo", lifo, "i", i, "j", j,
+                                "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
+                                "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
+                    }
+                }
+                for (int k = 0; k < oneLength; k++) {
+                    tracker = pool.borrowObject(one); 
+                    visitCount = tracker.getValidateCount();
+                    if (visitCount < cycleCount || visitCount > cycleCount + 1){
+                        fail(formatSettings("ONE", "runs", runs, "lifo", lifo, "i", i, "j", j,
+                                "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
+                                "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
+                    }
+                }
+                int visits[] = new int[twoLength];
+                for (int k = 0; k < twoLength; k++) {
+                    tracker = pool.borrowObject(two); 
+                    visitCount = tracker.getValidateCount();
+                    visits[k] = visitCount;
+                    if (visitCount < cycleCount || visitCount > cycleCount + 1){
+                        StringBuffer sb = new StringBuffer("Visits:");
+                        for (int l = 0; l <= k; l++){
+                            sb.append(visits[l]).append(' ');
+                        }
+                        fail(formatSettings("TWO "+sb.toString(), "runs", runs, "lifo", lifo, "i", i, "j", j,
+                                "k", k, "visitCount", visitCount, "cycleCount", cycleCount,
+                                "totalInstances", totalInstances, zeroLength, oneLength, twoLength));
+                    }
+                }
+            }
+        }
+    }
+    
+    public void testConstructors() {
+        
+        // Make constructor arguments all different from defaults
+        int maxActive = 1;
+        int maxIdle = 2;
+        long maxWait = 3;
+        int minIdle = 4;
+        int maxTotal = 5;
+        long minEvictableIdleTimeMillis = 6;
+        int numTestsPerEvictionRun = 7;
+        boolean testOnBorrow = true;
+        boolean testOnReturn = true;
+        boolean testWhileIdle = true;
+        long timeBetweenEvictionRunsMillis = 8;
+        byte whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+        boolean lifo = false;
+        
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>();
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_ACTIVE, pool.getMaxActive());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_WAIT, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
+                pool.getTestOnBorrow());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
+                pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
+                pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,
+                pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        GenericKeyedObjectPool.Config config = new GenericKeyedObjectPool.Config();
+        config.lifo = lifo;
+        config.maxActive = maxActive;
+        config.maxIdle = maxIdle;
+        config.minIdle = minIdle;
+        config.maxTotal = maxTotal;
+        config.maxWait = maxWait;
+        config.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+        config.numTestsPerEvictionRun = numTestsPerEvictionRun;
+        config.testOnBorrow = testOnBorrow;
+        config.testOnReturn = testOnReturn;
+        config.testWhileIdle = testWhileIdle;
+        config.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+        config.whenExhaustedAction = whenExhaustedAction;
+        pool = new GenericKeyedObjectPool<String, String>(null, config);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(minIdle, pool.getMinIdle());
+        assertEquals(maxTotal, pool.getMaxTotal());
+        assertEquals(minEvictableIdleTimeMillis,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(numTestsPerEvictionRun, pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow,pool.getTestOnBorrow());
+        assertEquals(testOnReturn,pool.getTestOnReturn());
+        assertEquals(testWhileIdle,pool.getTestWhileIdle());
+        assertEquals(timeBetweenEvictionRunsMillis,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(lifo, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_WAIT, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
+                pool.getTestOnBorrow());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
+                pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
+                pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION,
+                pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction, maxWait);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
+                pool.getTestOnBorrow());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
+                pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
+                pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                   maxWait, testOnBorrow, testOnReturn);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_IDLE, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow,pool.getTestOnBorrow());
+        assertEquals(testOnReturn,pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
+                pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                maxWait, maxIdle);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_BORROW,
+                pool.getTestOnBorrow());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_ON_RETURN,
+                pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
+                pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                maxWait, maxIdle, testOnBorrow, testOnReturn);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow, pool.getTestOnBorrow());
+        assertEquals(testOnReturn, pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TEST_WHILE_IDLE,
+                pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                maxWait, maxIdle, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
+                minEvictableIdleTimeMillis, testWhileIdle);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
+        assertEquals(minEvictableIdleTimeMillis,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(numTestsPerEvictionRun,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow, pool.getTestOnBorrow());
+        assertEquals(testOnReturn, pool.getTestOnReturn());
+        assertEquals(testWhileIdle,
+                pool.getTestWhileIdle());
+        assertEquals(timeBetweenEvictionRunsMillis,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                maxWait, maxIdle, maxTotal, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
+                minEvictableIdleTimeMillis, testWhileIdle);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_MIN_IDLE, pool.getMinIdle());
+        assertEquals(maxTotal, pool.getMaxTotal());
+        assertEquals(minEvictableIdleTimeMillis,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(numTestsPerEvictionRun,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow, pool.getTestOnBorrow());
+        assertEquals(testOnReturn, pool.getTestOnReturn());
+        assertEquals(testWhileIdle,
+                pool.getTestWhileIdle());
+        assertEquals(timeBetweenEvictionRunsMillis,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
+                minEvictableIdleTimeMillis, testWhileIdle);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(minIdle, pool.getMinIdle());
+        assertEquals(maxTotal, pool.getMaxTotal());
+        assertEquals(minEvictableIdleTimeMillis,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(numTestsPerEvictionRun,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow, pool.getTestOnBorrow());
+        assertEquals(testOnReturn, pool.getTestOnReturn());
+        assertEquals(testWhileIdle,
+                pool.getTestWhileIdle());
+        assertEquals(timeBetweenEvictionRunsMillis,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(GenericKeyedObjectPool.DEFAULT_LIFO, pool.getLifo());
+        
+        pool = new GenericKeyedObjectPool<String, String>(null, maxActive, whenExhaustedAction,
+                maxWait, maxIdle, maxTotal, minIdle, testOnBorrow, testOnReturn,
+                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
+                minEvictableIdleTimeMillis, testWhileIdle, lifo);
+        assertEquals(maxActive, pool.getMaxActive());
+        assertEquals(maxIdle, pool.getMaxIdle());
+        assertEquals(maxWait, pool.getMaxWait());
+        assertEquals(minIdle, pool.getMinIdle());
+        assertEquals(maxTotal, pool.getMaxTotal());
+        assertEquals(minEvictableIdleTimeMillis,
+                pool.getMinEvictableIdleTimeMillis());
+        assertEquals(numTestsPerEvictionRun,
+                pool.getNumTestsPerEvictionRun());
+        assertEquals(testOnBorrow, pool.getTestOnBorrow());
+        assertEquals(testOnReturn, pool.getTestOnReturn());
+        assertEquals(testWhileIdle,
+                pool.getTestWhileIdle());
+        assertEquals(timeBetweenEvictionRunsMillis,
+                pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
+        assertEquals(lifo, pool.getLifo());  
+    }
+    
+    public void testExceptionOnPassivateDuringReturn() throws Exception {
+        SimpleFactory<String> factory = new SimpleFactory<String>();        
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        String obj = pool.borrowObject("one");
+        factory.setThrowExceptionOnPassivate(true);
+        pool.returnObject("one", obj);
+        assertEquals(0,pool.getNumIdle());
+        pool.close();
+    }
+    
+    public void testExceptionOnDestroyDuringBorrow() throws Exception {
+        SimpleFactory<String> factory = new SimpleFactory<String>(); 
+        factory.setThrowExceptionOnDestroy(true);
+        factory.setValidationEnabled(true);
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        pool.setTestOnBorrow(true);
+        pool.borrowObject("one");
+        factory.setValid(false); // Make validation fail on next borrow attempt
+        try {
+            pool.borrowObject("one");
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // expected
+        }
+        assertEquals(1, pool.getNumActive("one"));
+        assertEquals(0, pool.getNumIdle("one"));
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+    }
+    
+    public void testExceptionOnDestroyDuringReturn() throws Exception {
+        SimpleFactory<String> factory = new SimpleFactory<String>(); 
+        factory.setThrowExceptionOnDestroy(true);
+        factory.setValidationEnabled(true);
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        pool.setTestOnReturn(true);
+        String obj1 = pool.borrowObject("one");
+        pool.borrowObject("one");
+        factory.setValid(false); // Make validation fail
+        pool.returnObject("one", obj1);
+        assertEquals(1, pool.getNumActive("one"));
+        assertEquals(0, pool.getNumIdle("one"));
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+    }
+    
+    public void testExceptionOnActivateDuringBorrow() throws Exception {
+        SimpleFactory<String> factory = new SimpleFactory<String>(); 
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        String obj1 = pool.borrowObject("one");
+        String obj2 = pool.borrowObject("one");
+        pool.returnObject("one", obj1);
+        pool.returnObject("one", obj2);
+        factory.setThrowExceptionOnActivate(true);
+        factory.setEvenValid(false);  
+        // Activation will now throw every other time
+        // First attempt throws, but loop continues and second succeeds
+        String obj = pool.borrowObject("one");
+        assertEquals(1, pool.getNumActive("one"));
+        assertEquals(0, pool.getNumIdle("one"));
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+        
+        pool.returnObject("one", obj);
+        factory.setValid(false);
+        // Validation will now fail on activation when borrowObject returns
+        // an idle instance, and then when attempting to create a new instance
+        try {
+            pool.borrowObject("one");
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // expected
+        }
+        assertEquals(0, pool.getNumActive("one"));
+        assertEquals(0, pool.getNumIdle("one"));
+        assertEquals(0, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+    }
+    
+    public void testBlockedKeyDoesNotBlockPool() throws Exception {
+        SimpleFactory<String> factory = new SimpleFactory<String>();
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(5000);
+        pool.setMaxActive(1);
+        pool.setMaxTotal(-1);
+        pool.borrowObject("one");
+        long start = System.currentTimeMillis();
+        // Needs to be in a separate thread as this will block
+        Runnable simple = new SimpleTestThread<String, String>(pool, "one");
+        (new Thread(simple)).start();
+        // This should be almost instant. If it isn't it means this thread got
+        // stuck behind the thread created above which is bad.
+        // Give other thread a chance to start
+        Thread.sleep(1000);
+        pool.borrowObject("two");
+        long end = System.currentTimeMillis();
+        // If it fails it will be more than 4000ms (5000 less the 1000 sleep)
+        // If it passes it should be almost instant
+        // Use 3000ms as the threshold - should avoid timing issues on most
+        // (all? platforms)
+        assertTrue ("Elapsed time: "+(end-start)+" should be less than 4000",(end-start) < 4000);
+        
+    }
+
+    private static final boolean DISPLAY_THREAD_DETAILS=
+        Boolean.valueOf(System.getProperty("TestGenericKeyedObjectPool.display.thread.details", "false")).booleanValue();
+    // To pass this to a Maven test, use:
+    // mvn test -DargLine="-DTestGenericKeyedObjectPool.display.thread.details=true"
+    // @see http://jira.codehaus.org/browse/SUREFIRE-121
+
+    /*
+     * Test multi-threaded pool access.
+     * Multiple keys, multiple threads, but maxActive only allows half the threads to succeed.
+     * 
+     * This test was prompted by Continuum build failures in the Commons DBCP test case:
+     * TestSharedPoolDataSource.testMultipleThreads2()
+     * Let's see if the this fails on Continuum too!
+     */
+    public void testMaxWaitMultiThreaded() throws Exception {
+        final long maxWait = 500; // wait for connection
+        final long holdTime = 2 * maxWait; // how long to hold connection
+        final int keyCount = 4; // number of different keys
+        final int threadsPerKey = 5; // number of threads to grab the key initially
+        SimpleFactory<String> factory = new SimpleFactory<String>();
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(maxWait);
+        pool.setMaxActive(threadsPerKey);
+        // Create enough threads so half the threads will have to wait
+        @SuppressWarnings("unchecked")
+        WaitingTestThread<String, String> wtt[] = new WaitingTestThread[keyCount * threadsPerKey * 2];
+        for(int i=0; i < wtt.length; i++){
+            wtt[i] = new WaitingTestThread<String, String>(pool,Integer.toString(i % keyCount),holdTime);
+        }
+        long origin = System.currentTimeMillis()-1000;
+        for(int i=0; i < wtt.length; i++){
+            wtt[i].start();
+        }
+        int failed = 0;
+        for(int i=0; i < wtt.length; i++){
+            wtt[i].join();
+            if (wtt[i]._thrown != null){
+                failed++;
+            }
+        }
+        if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){
+            System.out.println(
+                    "MaxWait: "+maxWait
+                    +" HoldTime: "+holdTime
+                    +" KeyCount: "+keyCount
+                    +" MaxActive: "+threadsPerKey
+                    +" Threads: "+wtt.length
+                    +" Failed: "+failed
+                    );
+            for(int i=0; i < wtt.length; i++){
+                WaitingTestThread<String, String> wt = wtt[i];
+                System.out.println(
+                        "Preborrow: "+(wt.preborrow-origin)
+                        + " Postborrow: "+(wt.postborrow != 0 ? wt.postborrow-origin : -1)
+                        + " BorrowTime: "+(wt.postborrow != 0 ? wt.postborrow-wt.preborrow : -1)
+                        + " PostReturn: "+(wt.postreturn != 0 ? wt.postreturn-origin : -1)
+                        + " Ended: "+(wt.ended-origin)
+                        + " Key: "+(wt._key)
+                        + " ObjId: "+wt.objectId
+                        );
+            }            
+        }
+        assertEquals("Expected half the threads to fail",wtt.length/2,failed);
+    }
+
+    /**
+     * Test case for POOL-180.
+     */
+    public void testMaxActivePerKeyExceeded() {
+        WaiterFactory<String> factory = new WaiterFactory<String>(0, 20, 0, 0, 0, 0, 8, 5, 0);
+        GenericKeyedObjectPool<String, Waiter> pool = new GenericKeyedObjectPool<String, Waiter>(factory);
+        pool.setMaxActive(5);
+        pool.setMaxTotal(8);
+        pool.setTestOnBorrow(true);
+        pool.setMaxIdle(5);
+        pool.setMaxWait(-1);
+        runTestThreads(pool, 20, 300, 250);
+    }
+    
+    /**
+     * POOL-192
+     * Verify that clear(key) does not leak capacity due to _numInternalProcessing
+     * not being decremented.
+     */
+    public void testClear() throws Exception {
+        SimpleFactory<String> factory = new SimpleFactory<String>();
+        GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<String, String>(factory);
+        pool.setMaxTotal(2);
+        pool.setMaxActive(2);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+        pool.addObject("one");
+        pool.addObject("one");
+        assertEquals(2, pool.getNumIdle());
+        pool.clear("one");
+        assertEquals(0, pool.getNumIdle());
+        assertEquals(0, pool.getNumIdle("one"));
+        String obj1 = pool.borrowObject("one");
+        String obj2 = pool.borrowObject("one");
+        pool.returnObject("one", obj1);
+        pool.returnObject("one", obj2);
+        pool.clear();
+        assertEquals(0, pool.getNumIdle());
+        assertEquals(0, pool.getNumIdle("one"));
+        pool.borrowObject("one");
+        pool.borrowObject("one");
+        pool.close();
+    }
+    
+    /*
+     * Very simple test thread that just tries to borrow an object from
+     * the provided pool with the specified key and returns it
+     */
+    static class SimpleTestThread<K, V> implements Runnable {
+        private final KeyedObjectPool<K, V> _pool;
+        private final K _key;
+        
+        public SimpleTestThread(KeyedObjectPool<K, V> pool, K key) {
+            _pool = pool;
+            _key = key;
+        }
+
+        public void run() {
+            try {
+                V obj = _pool.borrowObject(_key);
+                _pool.returnObject(_key, obj);
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    }
+    
+    /*
+     * Very simple test thread that just tries to borrow an object from
+     * the provided pool with the specified key and returns it after a wait
+     */
+    static class WaitingTestThread<K, V> extends Thread {
+        private final KeyedObjectPool<K, V> _pool;
+        private final K _key;
+        private final long _pause;
+        private Throwable _thrown;
+        
+        private long preborrow; // just before borrow
+        private long postborrow; //  borrow returned
+        private long postreturn; // after object was returned
+        private long ended;
+        private String objectId;
+
+        public WaitingTestThread(KeyedObjectPool<K, V> pool, K key, long pause) {
+            _pool = pool;
+            _key = key;
+            _pause = pause;
+            _thrown = null;
+        }
+
+        @Override
+        public void run() {
+            try {
+                preborrow = System.currentTimeMillis();
+                V obj = _pool.borrowObject(_key);
+                objectId=obj.toString();
+                postborrow = System.currentTimeMillis();
+                Thread.sleep(_pause);
+                _pool.returnObject(_key, obj);
+                postreturn = System.currentTimeMillis();
+            } catch (Exception e) {
+                _thrown = e;
+            } finally{
+                ended = System.currentTimeMillis();
+            }
+        }
+    }
+    
+    static class TestThread<V> implements Runnable {
+        private final java.util.Random _random = new java.util.Random();
+        
+        // Thread config items
+        private final KeyedObjectPool<String, V> _pool;
+        private final int _iter;
+        private final int _delay;
+
+        private volatile boolean _complete = false;
+        private volatile boolean _failed = false;
+        private volatile Exception _exception;
+
+        public TestThread(KeyedObjectPool<String, V> pool) {
+            this(pool, 100, 50);
+        }
+
+        public TestThread(KeyedObjectPool<String, V> pool, int iter) {
+            this(pool, iter, 50);
+        }
+
+        public TestThread(KeyedObjectPool<String, V> 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++) {
+                String key = String.valueOf(_random.nextInt(3));
+                try {
+                    Thread.sleep(_random.nextInt(_delay));
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+                V obj = null;
+                try {
+                    obj = _pool.borrowObject(key);
+                } catch(Exception e) {
+                    _exception = e;
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+
+                try {
+                    Thread.sleep(_random.nextInt(_delay));
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+                try {
+                    _pool.returnObject(key,obj);
+                } catch(Exception e) {
+                    _exception = e;
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+            }
+            _complete = true;
+        }
+    }
+
+    static class SimpleFactory<K> implements KeyedPoolableObjectFactory<K, String> {
+        public SimpleFactory() {
+            this(true);
+        }
+        public SimpleFactory(boolean valid) {
+            this.valid = valid;
+        }
+        public String makeObject(K key) {
+            synchronized(this) {
+                activeCount++;
+                if (activeCount > maxActive) {
+                    throw new IllegalStateException(
+                        "Too many active instances: " + activeCount);
+                }
+            }
+            return String.valueOf(key) + String.valueOf(counter++);
+        }
+        public void destroyObject(K key, String obj) throws Exception {
+            doWait(destroyLatency);
+            synchronized(this) {
+                activeCount--;
+            }
+            if (exceptionOnDestroy) {
+                throw new Exception();
+            }
+        }
+        public boolean validateObject(K key, String obj) {
+            if (enableValidation) { 
+                return validateCounter++%2 == 0 ? evenValid : oddValid; 
+            } else {
+                return valid;
+            }
+        }
+        public void activateObject(K key, String obj) throws Exception {
+            if (exceptionOnActivate) {
+                if (!(validateCounter++%2 == 0 ? evenValid : oddValid)) {
+                    throw new Exception();
+                }
+            }
+        }
+        public void passivateObject(K key, String obj) throws Exception {
+            if (exceptionOnPassivate) {
+                throw new Exception();
+            }
+        }
+        
+        public void setMaxActive(int maxActive) {
+            this.maxActive = maxActive;
+        }
+        public void setDestroyLatency(long destroyLatency) {
+            this.destroyLatency = destroyLatency;
+        }
+        public void setValidationEnabled(boolean b) {
+            enableValidation = b;
+        }
+        void setEvenValid(boolean valid) {
+            evenValid = valid;
+        }
+        void setValid(boolean valid) {
+            evenValid = valid;
+            oddValid = valid;
+        }
+        
+        public void setThrowExceptionOnActivate(boolean b) {
+            exceptionOnActivate = b;
+        }
+        
+        public void setThrowExceptionOnDestroy(boolean b) {
+            exceptionOnDestroy = b;
+        }
+        
+        public void setThrowExceptionOnPassivate(boolean b) {
+            exceptionOnPassivate = b;
+        }
+        
+        int counter = 0;
+        boolean valid;
+        
+        int activeCount = 0;
+        int validateCounter = 0;
+        boolean evenValid = true;
+        boolean oddValid = true;
+        boolean enableValidation = false;
+        long destroyLatency = 0;
+        int maxActive = Integer.MAX_VALUE;
+        boolean exceptionOnPassivate = false;
+        boolean exceptionOnActivate = false;
+        boolean exceptionOnDestroy = false;
+        
+        private void doWait(long latency) {
+            try {
+                Thread.sleep(latency);
+            } catch (InterruptedException ex) {
+                // ignore
+            }
+        }
+    }
+
+    @Override
+    protected boolean isLifo() {
+        return true;
+    }
+
+    @Override
+    protected boolean isFifo() {
+        return false;
+    }
+
+    private String getExceptionTrace(Throwable t){
+        StringWriter sw = new StringWriter();
+        t.printStackTrace(new PrintWriter(sw));
+        return sw.toString();
+    }
+    
+    private String formatSettings(String title, String s, int i, String s0, boolean b0, String s1, int i1, String s2, int i2, String s3, int i3,
+            String s4, int i4, String s5, int i5, String s6, int i6, int zeroLength, int oneLength, int twoLength){
+        StringBuffer sb = new StringBuffer(80);
+        sb.append(title).append(' ');
+        sb.append(s).append('=').append(i).append(' ');
+        sb.append(s0).append('=').append(b0).append(' ');
+        sb.append(s1).append('=').append(i1).append(' ');
+        sb.append(s2).append('=').append(i2).append(' ');
+        sb.append(s3).append('=').append(i3).append(' ');
+        sb.append(s4).append('=').append(i4).append(' ');
+        sb.append(s5).append('=').append(i5).append(' ');
+        sb.append(s6).append('=').append(i6).append(' ');
+        sb.append("Lengths=").append(zeroLength).append(',').append(oneLength).append(',').append(twoLength).append(' ');
+        return sb.toString();
+    }
+    
+}
+
+
diff --git a/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPoolFactory.java b/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPoolFactory.java
new file mode 100644
index 0000000..d35561d
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestGenericKeyedObjectPoolFactory.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.KeyedObjectPoolFactory;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+import org.apache.commons.pool.TestKeyedObjectPoolFactory;
+
+/**
+ * Tests for {@link GenericKeyedObjectPoolFactory}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestGenericKeyedObjectPoolFactory extends TestKeyedObjectPoolFactory {
+    public TestGenericKeyedObjectPoolFactory(final String name) {
+        super(name);
+    }
+
+    @Override
+    protected KeyedObjectPoolFactory<Object, Integer> makeFactory(final KeyedPoolableObjectFactory<Object, Integer> objectFactory) {
+        return new GenericKeyedObjectPoolFactory<Object, Integer>(objectFactory);
+    }
+
+    public void testConstructors() throws Exception {
+        GenericKeyedObjectPoolFactory<Object, Integer> factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory());
+        factory.createPool().close();
+        GenericKeyedObjectPool<Object, Integer> pool;
+
+
+        final GenericKeyedObjectPool.Config config = new GenericKeyedObjectPool.Config();
+        config.maxActive = 1;
+        config.maxIdle = 2;
+        config.maxWait = 3;
+        config.minIdle = 4;
+        config.minEvictableIdleTimeMillis = 5;
+        config.numTestsPerEvictionRun = 6;
+        config.testOnBorrow = true;
+        config.testOnReturn = false;
+        config.testWhileIdle = true;
+        config.timeBetweenEvictionRunsMillis = 8;
+        config.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+        config.lifo = false;
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), config);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxIdle());
+        assertEquals(3, pool.getMaxWait());
+        assertEquals(4, pool.getMinIdle());
+        assertEquals(5, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(6, pool.getNumTestsPerEvictionRun());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(true, pool.getTestWhileIdle());
+        assertEquals(false, pool.getLifo());
+        assertEquals(8, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK, 125);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK, pool.getWhenExhaustedAction());
+        assertEquals(125, pool.getMaxWait());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 2, true, false);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 2, 3);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, 4);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(4, pool.getMaxTotal());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, true, false);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, true, false, 4, 5, 6, false);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(4, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(5, pool.getNumTestsPerEvictionRun());
+        assertEquals(6, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(false, pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+
+
+        factory = new GenericKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(), 1, GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, 4, true, false, 5, 6, 7, true);
+        pool = (GenericKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(4, pool.getMaxTotal());
+        assertEquals(5, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(6, pool.getNumTestsPerEvictionRun());
+        assertEquals(7, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(true, pool.getTestWhileIdle());
+        assertEquals(GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.close();
+    }
+}
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..084dcbf
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestGenericObjectPool.java
@@ -0,0 +1,1827 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.math.BigInteger;
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+import org.apache.commons.pool.BasePoolableObjectFactory;
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolUtils;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.TestBaseObjectPool;
+import org.apache.commons.pool.VisitTracker;
+import org.apache.commons.pool.VisitTrackerFactory;
+
+/**
+ * @author Rodney Waldhoff
+ * @author Dirk Verbeeck
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestGenericObjectPool extends TestBaseObjectPool<String> {
+    public TestGenericObjectPool(String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected ObjectPool<String> makeEmptyPool(int mincap) {
+       GenericObjectPool<String> pool = new GenericObjectPool<String>(new SimpleFactory());
+       pool.setMaxActive(mincap);
+       pool.setMaxIdle(mincap);
+       return pool;
+    }
+
+    @Override
+    protected ObjectPool<Integer> makeEmptyPool(final PoolableObjectFactory<Integer> factory) {
+        return new GenericObjectPool<Integer>(factory);
+    }
+
+    @Override
+    protected String getNthObject(int n) {
+        return String.valueOf(n);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        pool = new GenericObjectPool<String>(new SimpleFactory());
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        pool.clear();
+        pool.close();
+        pool = null;
+    }
+
+    public void testWhenExhaustedGrow() throws Exception {
+        pool.setMaxActive(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
+        String obj1 = pool.borrowObject();
+        assertNotNull(obj1);
+        String obj2 = pool.borrowObject();
+        assertNotNull(obj2);
+        pool.returnObject(obj2);
+        pool.returnObject(obj1);
+        pool.close();
+    }
+
+    public void testWhenExhaustedFail() throws Exception {
+        pool.setMaxActive(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+        String obj1 = pool.borrowObject();
+        assertNotNull(obj1);
+        try {
+            pool.borrowObject();
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+        pool.returnObject(obj1);
+        assertEquals(1, pool.getNumIdle());
+        pool.close();
+    }
+
+    public void testWhenExhaustedBlock() throws Exception {
+        pool.setMaxActive(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(10L);
+        String obj1 = pool.borrowObject();
+        assertNotNull(obj1);
+        try {
+            pool.borrowObject();
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+        pool.returnObject(obj1);
+        pool.close();
+    }
+
+    public void testWhenExhaustedBlockInterupt() throws Exception {
+        pool.setMaxActive(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(0);
+        String obj1 = pool.borrowObject();
+        
+        // Make sure an object was obtained
+        assertNotNull(obj1);
+        
+        // Create a separate thread to try and borrow another object
+        WaitingTestThread<String> wtt = new WaitingTestThread<String>(pool, 200);
+        wtt.start();
+        // Give wtt time to start
+        Thread.sleep(200);
+        wtt.interrupt();
+
+        // Give interupt time to take effect
+        Thread.sleep(200);
+        
+        // Check thread was interrupted
+        assertTrue(wtt._thrown instanceof InterruptedException);
+
+        // Return object to the pool
+        pool.returnObject(obj1);
+        
+        // Bug POOL-162 - check there is now an object in the pool
+        pool.setMaxWait(10L);
+        String obj2 = null;
+        try {
+             obj2 = pool.borrowObject();
+            assertNotNull(obj2);
+        } catch(NoSuchElementException e) {
+            // Not expected
+            fail("NoSuchElementException not expected");
+        }
+        pool.returnObject(obj2);
+        pool.close();
+    }
+
+    public void testWhenExhaustedBlockClosePool() throws Exception {
+        pool.setMaxActive(1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(0);
+        Object obj1 = pool.borrowObject();
+        
+        // Make sure an object was obtained
+        assertNotNull(obj1);
+        
+        // Create a separate thread to try and borrow another object
+        WaitingTestThread<String> wtt = new WaitingTestThread<String>(pool, 200);
+        wtt.start();
+        // Give wtt time to start
+        Thread.sleep(200);
+        
+        // close the pool (Bug POOL-189)
+        pool.close();
+        
+        // Give interrupt time to take effect
+        Thread.sleep(200);
+        
+        // Check thread was interrupted
+        assertTrue(wtt._thrown instanceof IllegalStateException);
+    }
+
+    public void testEvictWhileEmpty() throws Exception {
+        pool.evict();
+        pool.evict();
+        pool.close();
+    }
+    
+    /**
+     * Tests addObject contention between ensureMinIdle triggered by
+     * the Evictor with minIdle > 0 and borrowObject. 
+     */
+    public void testEvictAddObjects() throws Exception {
+        SimpleFactory factory = new SimpleFactory();
+        factory.setMakeLatency(300);
+        factory.setMaxActive(2);
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        pool.setMaxActive(2);
+        pool.setMinIdle(1);
+        pool.borrowObject(); // numActive = 1, numIdle = 0
+        // Create a test thread that will run once and try a borrow after
+        // 150ms fixed delay
+        TestThread<String> borrower = new TestThread<String>(pool, 1, 150, false);
+        Thread borrowerThread = new Thread(borrower);
+        // Set evictor to run in 100 ms - will create idle instance
+        pool.setTimeBetweenEvictionRunsMillis(100);
+        borrowerThread.start();  // Off to the races
+        borrowerThread.join();
+        assertTrue(!borrower.failed());
+        pool.close();
+    }
+
+    public void testEvictLIFO() throws Exception {
+        checkEvict(true);   
+    }
+    
+    public void testEvictFIFO() throws Exception {
+        checkEvict(false);
+    }
+    
+    public void checkEvict(boolean lifo) throws Exception {
+        // yea this is hairy but it tests all the code paths in GOP.evict()
+        final SimpleFactory factory = new SimpleFactory();
+        final GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        pool.setSoftMinEvictableIdleTimeMillis(10);
+        pool.setMinIdle(2);
+        pool.setTestWhileIdle(true);
+        pool.setLifo(lifo);
+        PoolUtils.prefill(pool, 5);
+        pool.evict();
+        factory.setEvenValid(false);
+        factory.setOddValid(false);
+        factory.setThrowExceptionOnActivate(true);
+        pool.evict();
+        PoolUtils.prefill(pool, 5);
+        factory.setThrowExceptionOnActivate(false);
+        factory.setThrowExceptionOnPassivate(true);
+        pool.evict();
+        factory.setThrowExceptionOnPassivate(false);
+        factory.setEvenValid(true);
+        factory.setOddValid(true);
+        Thread.sleep(125);
+        pool.evict();
+        assertEquals(2, pool.getNumIdle());
+    }
+    
+    /**
+     * Test to make sure evictor visits least recently used objects first,
+     * regardless of FIFO/LIFO 
+     * 
+     * JIRA: POOL-86
+     */ 
+    public void testEvictionOrder() throws Exception {
+        checkEvictionOrder(false);
+        checkEvictionOrder(true);
+    }
+    
+    private void checkEvictionOrder(boolean lifo) throws Exception {
+        SimpleFactory factory = new SimpleFactory();
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        pool.setNumTestsPerEvictionRun(2);
+        pool.setMinEvictableIdleTimeMillis(100);
+        pool.setLifo(lifo);
+        for (int i = 0; i < 5; i++) {
+            pool.addObject();
+            Thread.sleep(100);
+        }
+        // Order, oldest to youngest, is "0", "1", ...,"4"
+        pool.evict(); // Should evict "0" and "1"
+        Object obj = pool.borrowObject();
+        assertTrue("oldest not evicted", !obj.equals("0"));
+        assertTrue("second oldest not evicted", !obj.equals("1"));
+        // 2 should be next out for FIFO, 4 for LIFO
+        assertEquals("Wrong instance returned", lifo ? "4" : "2" , obj); 
+        
+        // Two eviction runs in sequence
+        factory = new SimpleFactory();
+        pool = new GenericObjectPool<String>(factory);
+        pool.setNumTestsPerEvictionRun(2);
+        pool.setMinEvictableIdleTimeMillis(100);
+        pool.setLifo(lifo);
+        for (int i = 0; i < 5; i++) {
+            pool.addObject();
+            Thread.sleep(100);
+        }
+        pool.evict(); // Should evict "0" and "1"
+        pool.evict(); // Should evict "2" and "3"
+        obj = pool.borrowObject();
+        assertEquals("Wrong instance remaining in pool", "4", obj);     
+    }
+    
+    /**
+     * Verifies that the evictor visits objects in expected order
+     * and frequency. 
+     */
+    public void testEvictorVisiting() throws Exception {
+        checkEvictorVisiting(true);
+        checkEvictorVisiting(false);  
+    }
+    
+    private void checkEvictorVisiting(boolean lifo) throws Exception {
+        VisitTrackerFactory<Object> factory = new VisitTrackerFactory<Object>();
+        GenericObjectPool<VisitTracker<Object>> pool = new GenericObjectPool<VisitTracker<Object>>(factory);
+        pool.setNumTestsPerEvictionRun(2);
+        pool.setMinEvictableIdleTimeMillis(-1);
+        pool.setTestWhileIdle(true);
+        pool.setLifo(lifo);
+        pool.setTestOnReturn(false);
+        pool.setTestOnBorrow(false);
+        for (int i = 0; i < 8; i++) {
+            pool.addObject();
+        }
+        pool.evict(); // Visit oldest 2 - 0 and 1
+        VisitTracker<Object> obj = pool.borrowObject();
+        pool.returnObject(obj);
+        obj = pool.borrowObject();
+        pool.returnObject(obj);
+        //  borrow, return, borrow, return 
+        //  FIFO will move 0 and 1 to end
+        //  LIFO, 7 out, then in, then out, then in
+        pool.evict();  // Should visit 2 and 3 in either case
+        for (int i = 0; i < 8; i++) {
+            VisitTracker<Object> tracker = pool.borrowObject();    
+            if (tracker.getId() >= 4) {
+                assertEquals("Unexpected instance visited " + tracker.getId(),
+                        0, tracker.getValidateCount());
+            } else {
+                assertEquals("Instance " +  tracker.getId() + 
+                        " visited wrong number of times.",
+                        1, tracker.getValidateCount());
+            }
+        } 
+
+        factory = new VisitTrackerFactory<Object>();
+        pool = new GenericObjectPool<VisitTracker<Object>>(factory);
+        pool.setNumTestsPerEvictionRun(3);
+        pool.setMinEvictableIdleTimeMillis(-1);
+        pool.setTestWhileIdle(true);
+        pool.setLifo(lifo);
+        pool.setTestOnReturn(false);
+        pool.setTestOnBorrow(false);
+        for (int i = 0; i < 8; i++) {
+            pool.addObject();
+        }
+        pool.evict(); // 0, 1, 2
+        pool.evict(); // 3, 4, 5
+        obj = pool.borrowObject();
+        pool.returnObject(obj);
+        obj = pool.borrowObject();
+        pool.returnObject(obj);
+        obj = pool.borrowObject();
+        pool.returnObject(obj);
+        // borrow, return, borrow, return 
+        //  FIFO 3,4,5,6,7,0,1,2
+        //  LIFO 7,6,5,4,3,2,1,0
+        // In either case, pointer should be at 6
+        pool.evict();
+        // Should hit 6,7,0 - 0 for second time
+        for (int i = 0; i < 8; i++) {
+            VisitTracker<Object> tracker = pool.borrowObject();
+            if (tracker.getId() != 0) {
+                assertEquals("Instance " +  tracker.getId() + 
+                        " visited wrong number of times.",
+                        1, tracker.getValidateCount());
+            } else {
+                assertEquals("Instance " +  tracker.getId() + 
+                        " visited wrong number of times.",
+                        2, tracker.getValidateCount());
+            }
+        } 
+        // Randomly generate a pools with random numTests
+        // and make sure evictor cycles through elements appropriately
+        int[] smallPrimes = {2, 3, 5, 7};
+        Random random = new Random();
+        random.setSeed(System.currentTimeMillis());
+        for (int i = 0; i < 4; i++) {
+            pool.setNumTestsPerEvictionRun(smallPrimes[i]);
+            for (int j = 0; j < 5; j++) {
+                pool = new GenericObjectPool<VisitTracker<Object>>(factory);
+                pool.setNumTestsPerEvictionRun(3);
+                pool.setMinEvictableIdleTimeMillis(-1);
+                pool.setTestWhileIdle(true);
+                pool.setLifo(lifo);
+                pool.setTestOnReturn(false);
+                pool.setTestOnBorrow(false);
+                pool.setMaxIdle(-1);
+                int instanceCount = 10 + random.nextInt(20);
+                pool.setMaxActive(instanceCount);
+                for (int k = 0; k < instanceCount; k++) {
+                    pool.addObject();
+                }
+
+                // Execute a random number of evictor runs
+                int runs = 10 + random.nextInt(50);
+                for (int k = 0; k < runs; k++) {
+                    pool.evict();
+                }
+
+                // Number of times evictor should have cycled through the pool
+                int cycleCount = (runs * pool.getNumTestsPerEvictionRun())
+                / instanceCount;
+
+                // Look at elements and make sure they are visited cycleCount
+                // or cycleCount + 1 times
+                VisitTracker<Object> tracker = null;
+                int visitCount = 0;
+                for (int k = 0; k < instanceCount; k++) {
+                    tracker = pool.borrowObject(); 
+                    assertTrue(pool.getNumActive() <= pool.getMaxActive());
+                    visitCount = tracker.getValidateCount();                  
+                    assertTrue(visitCount >= cycleCount && 
+                            visitCount <= cycleCount + 1);
+                }
+            }
+        }
+    }
+
+    public void testExceptionOnPassivateDuringReturn() throws Exception {
+        SimpleFactory factory = new SimpleFactory();        
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        String obj = pool.borrowObject();
+        factory.setThrowExceptionOnPassivate(true);
+        pool.returnObject(obj);
+        assertEquals(0,pool.getNumIdle());
+        pool.close();
+    }
+    
+    public void testExceptionOnDestroyDuringBorrow() throws Exception {
+        SimpleFactory factory = new SimpleFactory(); 
+        factory.setThrowExceptionOnDestroy(true);
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        pool.setTestOnBorrow(true);
+        pool.borrowObject();
+        factory.setValid(false); // Make validation fail on next borrow attempt
+        try {
+            pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // expected
+        }
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+    }
+    
+    public void testExceptionOnDestroyDuringReturn() throws Exception {
+        SimpleFactory factory = new SimpleFactory(); 
+        factory.setThrowExceptionOnDestroy(true);
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        pool.setTestOnReturn(true);
+        String obj1 = pool.borrowObject();
+        pool.borrowObject();
+        factory.setValid(false); // Make validation fail
+        pool.returnObject(obj1);
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+    }
+    
+    public void testExceptionOnActivateDuringBorrow() throws Exception {
+        SimpleFactory factory = new SimpleFactory(); 
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        String obj1 = pool.borrowObject();
+        String obj2 = pool.borrowObject();
+        pool.returnObject(obj1);
+        pool.returnObject(obj2);
+        factory.setThrowExceptionOnActivate(true);
+        factory.setEvenValid(false);  
+        // Activation will now throw every other time
+        // First attempt throws, but loop continues and second succeeds
+        String obj = pool.borrowObject();
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+        
+        pool.returnObject(obj);
+        factory.setValid(false);
+        // Validation will now fail on activation when borrowObject returns
+        // an idle instance, and then when attempting to create a new instance
+        try {
+            obj1 = pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // expected
+        }
+        assertEquals(0, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+    }
+
+    public void testSetFactoryWithActiveObjects() throws Exception {
+        GenericObjectPool<String> pool = new GenericObjectPool<String>();
+        pool.setMaxIdle(10);
+        pool.setFactory(new SimpleFactory());
+        String obj = pool.borrowObject();
+        assertNotNull(obj);
+        try {
+            pool.setFactory(null);
+            fail("Expected IllegalStateException");
+        } catch(IllegalStateException e) {
+            // expected
+        }
+        try {
+            pool.setFactory(new SimpleFactory());
+            fail("Expected IllegalStateException");
+        } catch(IllegalStateException e) {
+            // expected
+        }
+    }
+
+    public void testSetFactoryWithNoActiveObjects() throws Exception {
+        GenericObjectPool<String> pool = new GenericObjectPool<String>();
+        pool.setMaxIdle(10);
+        pool.setFactory(new SimpleFactory());
+        String obj = pool.borrowObject();
+        pool.returnObject(obj);
+        assertEquals(1,pool.getNumIdle());
+        pool.setFactory(new SimpleFactory());
+        assertEquals(0,pool.getNumIdle());
+    }
+    
+    public void testNegativeMaxActive() throws Exception {
+        pool.setMaxActive(-1);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+        String obj = pool.borrowObject();
+        assertEquals(getNthObject(0),obj);
+        pool.returnObject(obj);
+    }
+
+    public void testMaxIdle() throws Exception {
+        pool.setMaxActive(100);
+        pool.setMaxIdle(8);
+        String[] active = new String[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject();
+        }
+        assertEquals(100,pool.getNumActive());
+        assertEquals(0,pool.getNumIdle());
+        for(int i=0;i<100;i++) {
+            pool.returnObject(active[i]);
+            assertEquals(99 - i,pool.getNumActive());
+            assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle());
+        }
+    }
+
+    public void testMaxIdleZero() throws Exception {
+        pool.setMaxActive(100);
+        pool.setMaxIdle(0);
+        String[] active = new String[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject();
+        }
+        assertEquals(100,pool.getNumActive());
+        assertEquals(0,pool.getNumIdle());
+        for(int i=0;i<100;i++) {
+            pool.returnObject(active[i]);
+            assertEquals(99 - i,pool.getNumActive());
+            assertEquals(0, pool.getNumIdle());
+        }
+    }
+
+    public void testMaxActive() throws Exception {
+        pool.setMaxActive(3);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+
+        pool.borrowObject();
+        pool.borrowObject();
+        pool.borrowObject();
+        try {
+            pool.borrowObject();
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+    
+    public void testTimeoutNoLeak() throws Exception {
+        pool.setMaxActive(2);
+        pool.setMaxWait(10);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        String obj = pool.borrowObject();
+        String obj2 = pool.borrowObject();
+        try {
+            pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            //xpected
+        }
+        pool.returnObject(obj2);
+        pool.returnObject(obj);
+        
+        obj = pool.borrowObject();
+        obj2 = pool.borrowObject();
+    }
+
+    public void testMaxActiveZero() throws Exception {
+        pool.setMaxActive(0);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+
+        try {
+            pool.borrowObject();
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    public void testMaxActiveUnderLoad() {
+        // Config
+        int numThreads = 199; // And main thread makes a round 200.
+        int numIter = 20;
+        int delay = 25;
+        int maxActive = 10;
+        
+        SimpleFactory factory = new SimpleFactory();
+        factory.setMaxActive(maxActive);
+        pool.setFactory(factory);
+        pool.setMaxActive(maxActive);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setTimeBetweenEvictionRunsMillis(-1);
+        
+        // Start threads to borrow objects
+        @SuppressWarnings("unchecked")
+        TestThread<String>[] threads = new TestThread[numThreads];
+        for(int i=0;i<numThreads;i++) {
+            // Factor of 2 on iterations so main thread does work whilst other
+            // threads are running. Factor of 2 on delay so average delay for
+            // other threads == actual delay for main thread
+            threads[i] = new TestThread<String>(pool, numIter * 2, delay * 2);
+            Thread t = new Thread(threads[i]);
+            t.start();
+        }
+        // Give the threads a chance to start doing some work
+        try {
+            Thread.sleep(5000);
+        } catch(InterruptedException e) {
+            // ignored
+        }
+        
+        for (int i = 0; i < numIter; i++) {
+            String obj = null;
+            try {
+                try {
+                    Thread.sleep(delay);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+                obj = pool.borrowObject();
+                // Under load, observed _numActive > _maxActive 
+                if (pool.getNumActive() > pool.getMaxActive()) {
+                    throw new IllegalStateException("Too many active objects");
+                }
+                try {
+                    Thread.sleep(delay);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+            } catch (Exception e) {
+                // Shouldn't happen
+                e.printStackTrace();
+                fail("Exception on borrow");
+            } finally {
+                if (obj != null) {
+                    try {
+                        pool.returnObject(obj);
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+
+        for(int i=0;i<numThreads;i++) {
+            while(!(threads[i]).complete()) {
+                try {
+                    Thread.sleep(500L);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+            }
+            if(threads[i].failed()) {
+                fail("Thread "+i+" failed: "+threads[i]._error.toString());
+            }
+        }
+    }
+
+    public void testInvalidWhenExhaustedAction() throws Exception {
+        try {
+            pool.setWhenExhaustedAction(Byte.MAX_VALUE);
+            fail("Expected IllegalArgumentException");
+        } catch(IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            ObjectPool<String> pool = new GenericObjectPool<String>(
+                new SimpleFactory(),
+                GenericObjectPool.DEFAULT_MAX_ACTIVE, 
+                Byte.MAX_VALUE,
+                GenericObjectPool.DEFAULT_MAX_WAIT, 
+                GenericObjectPool.DEFAULT_MAX_IDLE,
+                false,
+                false,
+                GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+                GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+                GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+                false
+            );
+            assertNotNull(pool);
+            fail("Expected IllegalArgumentException");
+        } catch(IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    public void testSettersAndGetters() throws Exception {
+        GenericObjectPool<String> pool = new GenericObjectPool<String>();
+        {
+            pool.setFactory(new SimpleFactory());
+        }
+        {
+            pool.setMaxActive(123);
+            assertEquals(123,pool.getMaxActive());
+        }
+        {
+            pool.setMaxIdle(12);
+            assertEquals(12,pool.getMaxIdle());
+        }
+        {
+            pool.setMaxWait(1234L);
+            assertEquals(1234L,pool.getMaxWait());
+        }
+        {
+            pool.setMinEvictableIdleTimeMillis(12345L);
+            assertEquals(12345L,pool.getMinEvictableIdleTimeMillis());
+        }
+        {
+            pool.setNumTestsPerEvictionRun(11);
+            assertEquals(11,pool.getNumTestsPerEvictionRun());
+        }
+        {
+            pool.setTestOnBorrow(true);
+            assertTrue(pool.getTestOnBorrow());
+            pool.setTestOnBorrow(false);
+            assertTrue(!pool.getTestOnBorrow());
+        }
+        {
+            pool.setTestOnReturn(true);
+            assertTrue(pool.getTestOnReturn());
+            pool.setTestOnReturn(false);
+            assertTrue(!pool.getTestOnReturn());
+        }
+        {
+            pool.setTestWhileIdle(true);
+            assertTrue(pool.getTestWhileIdle());
+            pool.setTestWhileIdle(false);
+            assertTrue(!pool.getTestWhileIdle());
+        }
+        {
+            pool.setTimeBetweenEvictionRunsMillis(11235L);
+            assertEquals(11235L,pool.getTimeBetweenEvictionRunsMillis());
+        }
+        {
+            pool.setSoftMinEvictableIdleTimeMillis(12135L);
+            assertEquals(12135L,pool.getSoftMinEvictableIdleTimeMillis());
+        }
+        {
+            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+            assertEquals(GenericObjectPool.WHEN_EXHAUSTED_BLOCK,pool.getWhenExhaustedAction());
+            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_FAIL);
+            assertEquals(GenericObjectPool.WHEN_EXHAUSTED_FAIL,pool.getWhenExhaustedAction());
+            pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
+            assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW,pool.getWhenExhaustedAction());
+        }
+    }
+    
+    public void testDefaultConfiguration() throws Exception {
+        GenericObjectPool<Object> pool = new GenericObjectPool<Object>();
+        assertConfiguration(new GenericObjectPool.Config(),pool);
+    }
+
+    public void testConstructors() throws Exception {
+        {
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>();
+            assertConfiguration(new GenericObjectPool.Config(),pool);
+        }
+        {
+            GenericObjectPool<String> pool = new GenericObjectPool<String>(new SimpleFactory());
+            assertConfiguration(new GenericObjectPool.Config(),pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxIdle = 3;
+            expected.maxWait = 5L;
+            expected.minEvictableIdleTimeMillis = 7L;
+            expected.numTestsPerEvictionRun = 9;
+            expected.testOnBorrow = true;
+            expected.testOnReturn = true;
+            expected.testWhileIdle = true;
+            expected.timeBetweenEvictionRunsMillis = 11L;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxWait = 5L;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxWait = 5L;
+            expected.testOnBorrow = true;
+            expected.testOnReturn = true;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait,expected.testOnBorrow,expected.testOnReturn);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxIdle = 3;
+            expected.maxWait = 5L;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait,expected.maxIdle);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxIdle = 3;
+            expected.maxWait = 5L;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            expected.testOnBorrow = true;
+            expected.testOnReturn = true;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive,expected.whenExhaustedAction,expected.maxWait,expected.maxIdle,expected.testOnBorrow,expected.testOnReturn);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxIdle = 3;
+            expected.maxWait = 5L;
+            expected.minEvictableIdleTimeMillis = 7L;
+            expected.numTestsPerEvictionRun = 9;
+            expected.testOnBorrow = true;
+            expected.testOnReturn = true;
+            expected.testWhileIdle = true;
+            expected.timeBetweenEvictionRunsMillis = 11L;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive, expected.whenExhaustedAction, expected.maxWait, expected.maxIdle, expected.testOnBorrow, expected.testOnReturn, expected.timeBetweenEvictionRunsMillis, expected.numTestsPerEvictionRun, expected.minEvictableIdleTimeMillis, expected.testWhileIdle);
+            assertConfiguration(expected,pool);
+        }
+        {
+            GenericObjectPool.Config expected = new GenericObjectPool.Config();
+            expected.maxActive = 2;
+            expected.maxIdle = 3;
+            expected.minIdle = 1;
+            expected.maxWait = 5L;
+            expected.minEvictableIdleTimeMillis = 7L;
+            expected.numTestsPerEvictionRun = 9;
+            expected.testOnBorrow = true;
+            expected.testOnReturn = true;
+            expected.testWhileIdle = true;
+            expected.timeBetweenEvictionRunsMillis = 11L;
+            expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+            GenericObjectPool<Object> pool = new GenericObjectPool<Object>(null,expected.maxActive, expected.whenExhaustedAction, expected.maxWait, expected.maxIdle, expected.minIdle, expected.testOnBorrow, expected.testOnReturn, expected.timeBetweenEvictionRunsMillis, expected.numTestsPerEvictionRun, expected.minEvictableIdleTimeMillis, expected.testWhileIdle);
+            assertConfiguration(expected,pool);
+        }
+    }
+
+    public void testSetConfig() throws Exception {
+        GenericObjectPool.Config expected = new GenericObjectPool.Config();
+        GenericObjectPool<Object> pool = new GenericObjectPool<Object>();
+        assertConfiguration(expected,pool);
+        expected.maxActive = 2;
+        expected.maxIdle = 3;
+        expected.maxWait = 5L;
+        expected.minEvictableIdleTimeMillis = 7L;
+        expected.numTestsPerEvictionRun = 9;
+        expected.testOnBorrow = true;
+        expected.testOnReturn = true;
+        expected.testWhileIdle = true;
+        expected.timeBetweenEvictionRunsMillis = 11L;
+        expected.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+        pool.setConfig(expected);
+        assertConfiguration(expected,pool);
+    }
+
+    public void testDebugInfo() throws Exception {
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(new SimpleFactory());
+        pool.setMaxIdle(3);
+        assertNotNull(pool.debugInfo());
+        String obj = pool.borrowObject();
+        assertNotNull(pool.debugInfo());
+        pool.returnObject(obj);
+        assertNotNull(pool.debugInfo());
+    }
+
+    public void testStartAndStopEvictor() throws Exception {
+        // set up pool without evictor
+        pool.setMaxIdle(6);
+        pool.setMaxActive(6);
+        pool.setNumTestsPerEvictionRun(6);
+        pool.setMinEvictableIdleTimeMillis(100L);
+
+        for(int j=0;j<2;j++) {
+            // populate the pool
+            {
+                String[] active = new String[6];
+                for(int i=0;i<6;i++) {
+                    active[i] = pool.borrowObject();
+                }
+                for(int i=0;i<6;i++) {
+                    pool.returnObject(active[i]);
+                }
+            }
+    
+            // note that it stays populated
+            assertEquals("Should have 6 idle",6,pool.getNumIdle());
+    
+            // start the evictor
+            pool.setTimeBetweenEvictionRunsMillis(50L);
+            
+            // wait a second (well, .2 seconds)
+            try { Thread.sleep(200L); } catch(InterruptedException e) { }
+            
+            // assert that the evictor has cleared out the pool
+            assertEquals("Should have 0 idle",0,pool.getNumIdle());
+    
+            // stop the evictor 
+            pool.startEvictor(0L);
+        }
+    }
+
+    public void testEvictionWithNegativeNumTests() throws Exception {
+        // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test
+        pool.setMaxIdle(6);
+        pool.setMaxActive(6);
+        pool.setNumTestsPerEvictionRun(-2);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(100L);
+
+        String[] active = new String[6];
+        for(int i=0;i<6;i++) {
+            active[i] = pool.borrowObject();
+        }
+        for(int i=0;i<6;i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.sleep(100L); } catch(InterruptedException e) { }
+        assertTrue("Should at most 6 idle, found " + pool.getNumIdle(),pool.getNumIdle() <= 6);
+        try { Thread.sleep(100L); } catch(InterruptedException e) { }
+        assertTrue("Should at most 3 idle, found " + pool.getNumIdle(),pool.getNumIdle() <= 3);
+        try { Thread.sleep(100L); } catch(InterruptedException e) { }
+        assertTrue("Should be at most 2 idle, found " + pool.getNumIdle(),pool.getNumIdle() <= 2);
+        try { Thread.sleep(100L); } catch(InterruptedException e) { }
+        assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
+    }
+
+    public void testEviction() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMaxActive(500);
+        pool.setNumTestsPerEvictionRun(100);
+        pool.setMinEvictableIdleTimeMillis(250L);
+        pool.setTimeBetweenEvictionRunsMillis(500L);
+        pool.setTestWhileIdle(true);
+
+        String[] active = new String[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.sleep(1000L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
+
+        for(int i=0;i<500;i++) {
+            active[i] = pool.borrowObject();
+        }
+        for(int i=0;i<500;i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.sleep(1000L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
+        try { Thread.sleep(600L); } catch(InterruptedException e) { }
+        assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
+    }
+ 
+    public void testEvictionSoftMinIdle() throws Exception {
+        class TimeTest extends BasePoolableObjectFactory<TimeTest> {
+            private final long createTime;
+            public TimeTest() {
+                createTime = System.currentTimeMillis();
+            }
+            @Override
+            public TimeTest makeObject() throws Exception {
+                return new TimeTest();
+            }
+            public long getCreateTime() {
+                return createTime;
+            }
+        }
+        
+        GenericObjectPool<TimeTest> pool = null;        
+        pool = new GenericObjectPool<TimeTest>(new TimeTest());
+        
+        pool.setMaxIdle(5);
+        pool.setMaxActive(5);
+        pool.setNumTestsPerEvictionRun(5);
+        pool.setMinEvictableIdleTimeMillis(3000L);
+        pool.setSoftMinEvictableIdleTimeMillis(1000L);
+        pool.setMinIdle(2);
+
+        TimeTest[] active = new TimeTest[5];
+        Long[] creationTime = new Long[5] ;
+        for(int i=0;i<5;i++) {
+            active[i] = pool.borrowObject();
+            creationTime[i] = new Long(active[i].getCreateTime());
+        }
+        
+        for(int i=0;i<5;i++) {
+            pool.returnObject(active[i]);
+        }
+
+        // Soft evict all but minIdle(2)
+        Thread.sleep(1500L);
+        pool.evict();
+        assertEquals("Idle count different than expected.", 2, pool.getNumIdle());
+
+        // Hard evict the rest.
+        Thread.sleep(2000L);
+        pool.evict();
+        assertEquals("Idle count different than expected.", 0, pool.getNumIdle());
+    }
+
+    public void testMinIdle() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMinIdle(5);
+        pool.setMaxActive(10);
+        pool.setNumTestsPerEvictionRun(0);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(100L);
+        pool.setTestWhileIdle(true);
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        String[] active = new String[5];
+        active[0] = pool.borrowObject();
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=1 ; i<5 ; i++) {
+            active[i] = pool.borrowObject();
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=0 ; i<5 ; i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
+    }
+
+    public void testMinIdleMaxActive() throws Exception {
+        pool.setMaxIdle(500);
+        pool.setMinIdle(5);
+        pool.setMaxActive(10);
+        pool.setNumTestsPerEvictionRun(0);
+        pool.setMinEvictableIdleTimeMillis(50L);
+        pool.setTimeBetweenEvictionRunsMillis(100L);
+        pool.setTestWhileIdle(true);
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        String[] active = new String[10];
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=0 ; i<5 ; i++) {
+            active[i] = pool.borrowObject();
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
+
+        for(int i=0 ; i<5 ; i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
+
+        for(int i=0 ; i<10 ; i++) {
+            active[i] = pool.borrowObject();
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
+
+        for(int i=0 ; i<10 ; i++) {
+            pool.returnObject(active[i]);
+        }
+
+        try { Thread.sleep(150L); } catch(InterruptedException e) { }
+        assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
+    }
+
+    /**
+     * Kicks off <numThreads> test threads, each of which will go through
+     * <iterations> borrow-return cycles with random delay times <= delay
+     * in between.
+     */
+    public void runTestThreads(int numThreads, int iterations, int delay) {
+        @SuppressWarnings("unchecked")
+        TestThread<String>[] threads = new TestThread[numThreads];
+        for(int i=0;i<numThreads;i++) {
+            threads[i] = new TestThread<String>(pool,iterations,delay);
+            Thread t = new Thread(threads[i]);
+            t.start();
+        }
+        for(int i=0;i<numThreads;i++) {
+            while(!(threads[i]).complete()) {
+                try {
+                    Thread.sleep(500L);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+            }
+            if(threads[i].failed()) {
+                fail("Thread "+i+" failed: "+threads[i]._error.toString());
+            }
+        }
+    }
+    
+    public void testThreaded1() throws Exception {
+        pool.setMaxActive(15);
+        pool.setMaxIdle(15);
+        pool.setMaxWait(1000L);
+        runTestThreads(20, 100, 50);
+    }
+    
+    /**
+     * Verifies that maxActive is not exceeded when factory destroyObject
+     * has high latency, testOnReturn is set and there is high incidence of
+     * validation failures. 
+     */
+    public void testMaxActiveInvariant() throws Exception {
+        int maxActive = 15;
+        SimpleFactory factory = new SimpleFactory();
+        factory.setEvenValid(false);     // Every other validation fails
+        factory.setDestroyLatency(100);  // Destroy takes 100 ms
+        factory.setMaxActive(maxActive); // (makes - destroys) bound
+        factory.setValidationEnabled(true);
+        pool = new GenericObjectPool<String>(factory);
+        pool.setMaxActive(maxActive);
+        pool.setMaxIdle(-1);
+        pool.setTestOnReturn(true);
+        pool.setMaxWait(1000L);
+        runTestThreads(5, 10, 50);
+    }
+
+    public void testConcurrentBorrowAndEvict() throws Exception {
+
+        pool.setMaxActive(1);
+        pool.addObject();
+
+        for( int i=0; i<5000; i++) {
+            ConcurrentBorrowAndEvictThread one =
+                    new ConcurrentBorrowAndEvictThread(true);
+            ConcurrentBorrowAndEvictThread two =
+                    new ConcurrentBorrowAndEvictThread(false);
+
+            one.start();
+            two.start();
+            one.join();
+            two.join();
+
+            pool.returnObject(one.obj);
+            
+            /* Uncomment this for a progress indication
+            if (i % 10 == 0) {
+                System.out.println(i/10);
+            }
+            */
+        }
+    }
+
+    private class ConcurrentBorrowAndEvictThread extends Thread {
+        private boolean borrow;
+        public String obj;
+        
+        public ConcurrentBorrowAndEvictThread(boolean borrow) {
+            this.borrow = borrow;
+        }
+
+        @Override
+        public void run() {
+            try {
+                if (borrow) {
+                    obj = pool.borrowObject();
+                } else {
+                    pool.evict();
+                }
+            } catch (Exception e) { /* Ignore */}
+        }
+    }
+
+    static class TestThread<T> implements Runnable {
+        private final java.util.Random _random = new java.util.Random();
+        
+        // Thread config items
+        private final ObjectPool<T> _pool;
+        private final int _iter;
+        private final int _delay;
+        private final boolean _randomDelay;
+        private final T _expectedObject;
+        
+        private volatile boolean _complete = false;
+        private volatile boolean _failed = false;
+        private volatile Throwable _error;
+
+        public TestThread(ObjectPool<T> pool) {
+            this(pool, 100, 50, true, null);
+        }
+
+        public TestThread(ObjectPool<T> pool, int iter) {
+            this(pool, iter, 50, true, null);
+        }
+
+        public TestThread(ObjectPool<T> pool, int iter, int delay) {
+            this(pool, iter, delay, true, null);
+        }
+        
+        public TestThread(ObjectPool<T> pool, int iter, int delay,
+                boolean randomDelay) {
+            this(pool, iter, delay, randomDelay, null);
+        }
+
+        public TestThread(ObjectPool<T> pool, int iter, int delay,
+                boolean randomDelay, T obj) {
+            _pool = pool;
+            _iter = iter;
+            _delay = delay;
+            _randomDelay = randomDelay;
+            _expectedObject = obj;
+        }
+
+        public boolean complete() {
+            return _complete;
+        }
+
+        public boolean failed() {
+            return _failed;
+        }
+
+        public void run() {
+            for(int i=0;i<_iter;i++) {
+                long delay = 
+                    _randomDelay ? (long)_random.nextInt(_delay) : _delay;
+                try {
+                    Thread.sleep(delay);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+                T obj = null;
+                try {
+                    obj = _pool.borrowObject();
+                } catch(Exception e) {
+                    _error = e;
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+
+                if (_expectedObject != null && !_expectedObject.equals(obj)) {
+                    _error = new Throwable("Expected: "+_expectedObject+ " found: "+obj);
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+                
+                try {
+                    Thread.sleep(delay);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+                try {
+                    _pool.returnObject(obj);
+                } catch(Exception e) {
+                    _error = e;
+                    _failed = true;
+                    _complete = true;
+                    break;
+                }
+            }
+            _complete = true;
+        }
+    }
+
+    public void testFIFO() throws Exception {
+        pool.setLifo(false);
+        pool.addObject(); // "0"
+        pool.addObject(); // "1"
+        pool.addObject(); // "2"
+        assertEquals("Oldest", "0", pool.borrowObject());
+        assertEquals("Middle", "1", pool.borrowObject());
+        assertEquals("Youngest", "2", pool.borrowObject());
+        assertEquals("new-3", "3", pool.borrowObject());
+        pool.returnObject("r");
+        assertEquals("returned", "r", pool.borrowObject());
+        assertEquals("new-4", "4", pool.borrowObject());
+    }
+    
+    public void testLIFO() throws Exception {
+        pool.setLifo(true);
+        pool.addObject(); // "0"
+        pool.addObject(); // "1"
+        pool.addObject(); // "2"
+        assertEquals("Youngest", "2", pool.borrowObject());
+        assertEquals("Middle", "1", pool.borrowObject());
+        assertEquals("Oldest", "0", pool.borrowObject());
+        assertEquals("new-3", "3", pool.borrowObject());
+        pool.returnObject("r");
+        assertEquals("returned", "r", pool.borrowObject());
+        assertEquals("new-4", "4", pool.borrowObject());
+    }
+
+    public void testAddObject() throws Exception {
+        assertEquals("should be zero idle", 0, pool.getNumIdle());
+        pool.addObject();
+        assertEquals("should be one idle", 1, pool.getNumIdle());
+        assertEquals("should be zero active", 0, pool.getNumActive());
+        String obj = pool.borrowObject();
+        assertEquals("should be zero idle", 0, pool.getNumIdle());
+        assertEquals("should be one active", 1, pool.getNumActive());
+        pool.returnObject(obj);
+        assertEquals("should be one idle", 1, pool.getNumIdle());
+        assertEquals("should be zero active", 0, pool.getNumActive());
+
+        ObjectPool<Object> op = new GenericObjectPool<Object>();
+        try {
+            op.addObject();
+            fail("Expected IllegalStateException when there is no factory.");
+        } catch (IllegalStateException ise) {
+            //expected
+        }
+        op.close();
+    }
+    
+    protected GenericObjectPool<String> pool = null;
+
+    private void assertConfiguration(GenericObjectPool.Config expected, GenericObjectPool<?> actual) throws Exception {
+        assertEquals("testOnBorrow",expected.testOnBorrow,actual.getTestOnBorrow());
+        assertEquals("testOnReturn",expected.testOnReturn,actual.getTestOnReturn());
+        assertEquals("testWhileIdle",expected.testWhileIdle,actual.getTestWhileIdle());
+        assertEquals("whenExhaustedAction",expected.whenExhaustedAction,actual.getWhenExhaustedAction());
+        assertEquals("maxActive",expected.maxActive,actual.getMaxActive());
+        assertEquals("maxIdle",expected.maxIdle,actual.getMaxIdle());
+        assertEquals("maxWait",expected.maxWait,actual.getMaxWait());
+        assertEquals("minEvictableIdleTimeMillis",expected.minEvictableIdleTimeMillis,actual.getMinEvictableIdleTimeMillis());
+        assertEquals("numTestsPerEvictionRun",expected.numTestsPerEvictionRun,actual.getNumTestsPerEvictionRun());
+        assertEquals("timeBetweenEvictionRunsMillis",expected.timeBetweenEvictionRunsMillis,actual.getTimeBetweenEvictionRunsMillis());
+    }
+
+    public class SimpleFactory implements PoolableObjectFactory<String> {
+        public SimpleFactory() {
+            this(true);
+        }
+        public SimpleFactory(boolean valid) {
+            this(valid,valid);
+        }
+        public SimpleFactory(boolean evalid, boolean ovalid) {
+            evenValid = evalid;
+            oddValid = ovalid;
+        }
+        public synchronized void setValid(boolean valid) {
+            setEvenValid(valid);
+            setOddValid(valid);            
+        }
+        public synchronized void setEvenValid(boolean valid) {
+            evenValid = valid;
+        }
+        public synchronized void setOddValid(boolean valid) {
+            oddValid = valid;
+        }
+        public synchronized void setThrowExceptionOnPassivate(boolean bool) {
+            exceptionOnPassivate = bool;
+        }
+        public synchronized void setMaxActive(int maxActive) {
+            this.maxActive = maxActive;
+        }
+        public synchronized void setDestroyLatency(long destroyLatency) {
+            this.destroyLatency = destroyLatency;
+        }
+        public synchronized void setMakeLatency(long makeLatency) {
+            this.makeLatency = makeLatency;
+        }
+        public synchronized void setValidateLatency(long validateLatency) {
+            this.validateLatency = validateLatency;
+        }
+        public String makeObject() { 
+            final long waitLatency;
+            synchronized(this) {
+                activeCount++;
+                if (activeCount > maxActive) {
+                    throw new IllegalStateException(
+                        "Too many active instances: " + activeCount);
+                }
+                waitLatency = makeLatency;
+            }
+            if (waitLatency > 0) {
+                doWait(waitLatency);
+            }
+            final int counter;
+            synchronized(this) {
+                counter = makeCounter++;
+            }
+            return String.valueOf(counter);
+        }
+        public void destroyObject(String obj) throws Exception {
+            final long waitLatency;
+            final boolean hurl;
+            synchronized(this) {
+                waitLatency = destroyLatency;
+                hurl = exceptionOnDestroy;
+            }
+            if (waitLatency > 0) {
+                doWait(waitLatency);
+            }
+            synchronized(this) {
+                activeCount--;
+            }
+            if (hurl) {
+                throw new Exception();
+            }
+        }
+        public boolean validateObject(String obj) {
+            final boolean validate;
+            final boolean evenTest;
+            final boolean oddTest;
+            final long waitLatency;
+            final int counter;
+            synchronized(this) {
+                validate = enableValidation;
+                evenTest = evenValid;
+                oddTest = oddValid;
+                counter = validateCounter++;
+                waitLatency = validateLatency;
+            }
+            if (waitLatency > 0) {
+                doWait(waitLatency);
+            }
+            if (validate) { 
+                return counter%2 == 0 ? evenTest : oddTest; 
+            }
+            else {
+                return true;
+            }
+        }
+        public void activateObject(String obj) throws Exception {
+            final boolean hurl;
+            final boolean evenTest;
+            final boolean oddTest;
+            final int counter;
+            synchronized(this) {
+                hurl = exceptionOnActivate;
+                evenTest = evenValid;
+                oddTest = oddValid;
+                counter = validateCounter++;
+            }
+            if (hurl) {
+                if (!(counter%2 == 0 ? evenTest : oddTest)) {
+                    throw new Exception();
+                }
+            }
+        }
+        public void passivateObject(String obj) throws Exception {
+            final boolean hurl;
+            synchronized(this) {
+                hurl = exceptionOnPassivate;
+            }
+            if (hurl) {
+                throw new Exception();
+            }
+        }
+        int makeCounter = 0;
+        int validateCounter = 0;
+        int activeCount = 0;
+        boolean evenValid = true;
+        boolean oddValid = true;
+        boolean exceptionOnPassivate = false;
+        boolean exceptionOnActivate = false;
+        boolean exceptionOnDestroy = false;
+        boolean enableValidation = true;
+        long destroyLatency = 0;
+        long makeLatency = 0;
+        long validateLatency = 0;
+        int maxActive = Integer.MAX_VALUE;
+
+        public synchronized boolean isThrowExceptionOnActivate() {
+            return exceptionOnActivate;
+        }
+
+        public synchronized void setThrowExceptionOnActivate(boolean b) {
+            exceptionOnActivate = b;
+        }
+        
+        public synchronized void setThrowExceptionOnDestroy(boolean b) {
+            exceptionOnDestroy = b;
+        }
+
+        public synchronized boolean isValidationEnabled() {
+            return enableValidation;
+        }
+
+        public synchronized void setValidationEnabled(boolean b) {
+            enableValidation = b;
+        }
+        
+        public synchronized int getMakeCounter() {
+            return makeCounter;
+        }
+        
+        private void doWait(long latency) {
+            try {
+                Thread.sleep(latency);
+            } catch (InterruptedException ex) {
+                // ignore
+            }
+        }
+    }
+    @Override
+    protected boolean isLifo() {
+        return true;
+    }
+
+    @Override
+    protected boolean isFifo() {
+        return false;
+    }
+
+    /*
+     * Note: This test relies on timing for correct execution. There *should* be
+     * enough margin for this to work correctly on most (all?) systems but be
+     * aware of this if you see a failure of this test.
+     */
+    public void testBorrowObjectFairness() {
+        // Config
+        int numThreads = 30;
+        int maxActive = 10;
+
+        SimpleFactory factory = new SimpleFactory();
+        factory.setMaxActive(maxActive);
+        pool.setFactory(factory);
+        pool.setMaxActive(maxActive);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setTimeBetweenEvictionRunsMillis(-1);
+
+        // Start threads to borrow objects
+        @SuppressWarnings("unchecked")
+        TestThread<String>[] threads = new TestThread[numThreads];
+        for(int i=0;i<numThreads;i++) {
+            threads[i] = new TestThread<String>(pool, 1, 2000, false, String.valueOf(i % maxActive));
+            Thread t = new Thread(threads[i]);
+            t.start();
+            // Short delay to ensure threads start in correct order
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+                fail(e.toString());
+            }
+        }
+
+        // Wait for threads to finish
+        for(int i=0;i<numThreads;i++) {
+            while(!(threads[i]).complete()) {
+                try {
+                    Thread.sleep(500L);
+                } catch(InterruptedException e) {
+                    // ignored
+                }
+            }
+            if(threads[i].failed()) {
+                fail("Thread "+i+" failed: "+threads[i]._error.toString());
+            }
+        }
+    }
+    
+    /**
+     * On first borrow, first object fails validation, second object is OK.
+     * Subsequent borrows are OK. This was POOL-152.
+     */
+    public void testBrokenFactoryShouldNotBlockPool() {
+        int maxActive = 1;
+        
+        SimpleFactory factory = new SimpleFactory();
+        factory.setMaxActive(maxActive);
+        pool.setFactory(factory);
+        pool.setMaxActive(maxActive);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setTestOnBorrow(true);
+        
+        // First borrow object will need to create a new object which will fail
+        // validation.
+        String obj = null;
+        Exception ex = null;
+        factory.setValid(false);
+        try {
+            obj = pool.borrowObject();
+        } catch (Exception e) {
+            ex = e;
+        }
+        // Failure expected
+        assertNotNull(ex);
+        assertTrue(ex instanceof NoSuchElementException);
+        assertNull(obj);
+
+        // Configure factory to create valid objects so subsequent borrows work
+        factory.setValid(true);
+        
+        // Subsequent borrows should be OK
+        try {
+            obj = pool.borrowObject();
+        } catch (Exception e1) {
+            fail();
+        }
+        assertNotNull(obj);
+        try {
+            pool.returnObject(obj);
+        } catch (Exception e) {
+            fail();
+        }
+        
+    }
+
+    /*
+     * Very simple test thread that just tries to borrow an object from
+     * the provided pool returns it after a wait
+     */
+    static class WaitingTestThread<T> extends Thread {
+        private final GenericObjectPool<T> _pool;
+        private final long _pause;
+        private Throwable _thrown;
+        
+        private long preborrow; // just before borrow
+        private long postborrow; //  borrow returned
+        private long postreturn; // after object was returned
+        private long ended;
+        private String objectId;
+
+        public WaitingTestThread(GenericObjectPool<T> pool, long pause) {
+            _pool = pool;
+            _pause = pause;
+            _thrown = null;
+        }
+
+        @Override
+        public void run() {
+            try {
+                preborrow = System.currentTimeMillis();
+                T obj = _pool.borrowObject();
+                objectId=obj.toString();
+                postborrow = System.currentTimeMillis();
+                Thread.sleep(_pause);
+                _pool.returnObject(obj);
+                postreturn = System.currentTimeMillis();
+            } catch (Exception e) {
+                _thrown = e;
+            } finally{
+                ended = System.currentTimeMillis();
+            }
+        }
+    }
+
+    private static final boolean DISPLAY_THREAD_DETAILS=
+        Boolean.valueOf(System.getProperty("TestGenericObjectPool.display.thread.details", "false")).booleanValue();
+    // To pass this to a Maven test, use:
+    // mvn test -DargLine="-DTestGenericObjectPool.display.thread.details=true"
+    // @see http://jira.codehaus.org/browse/SUREFIRE-121
+
+    /*
+     * Test multi-threaded pool access.
+     * Multiple threads, but maxActive only allows half the threads to succeed.
+     * 
+     * This test was prompted by Continuum build failures in the Commons DBCP test case:
+     * TestPerUserPoolDataSource.testMultipleThreads2()
+     * Let's see if the this fails on Continuum too!
+     */
+    public void testMaxWaitMultiThreaded() throws Exception {
+        final long maxWait = 500; // wait for connection
+        final long holdTime = 2 * maxWait; // how long to hold connection
+        final int threads = 10; // number of threads to grab the object initially
+        SimpleFactory factory = new SimpleFactory();
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory);
+        pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
+        pool.setMaxWait(maxWait);
+        pool.setMaxActive(threads);
+        // Create enough threads so half the threads will have to wait
+        @SuppressWarnings("unchecked")
+        WaitingTestThread<String> wtt[] = new WaitingTestThread[threads * 2];
+        for(int i=0; i < wtt.length; i++){
+            wtt[i] = new WaitingTestThread<String>(pool,holdTime);
+        }
+        long origin = System.currentTimeMillis()-1000;
+        for(int i=0; i < wtt.length; i++){
+            wtt[i].start();
+        }
+        int failed = 0;
+        for(int i=0; i < wtt.length; i++){
+            wtt[i].join();
+            if (wtt[i]._thrown != null){
+                failed++;
+            }
+        }
+        if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){
+            System.out.println(
+                    "MaxWait: "+maxWait
+                    +" HoldTime: "+holdTime
+                    + " MaxActive: "+threads
+                    +" Threads: "+wtt.length
+                    +" Failed: "+failed
+                    );
+            for(int i=0; i < wtt.length; i++){
+                WaitingTestThread<String> wt = wtt[i];
+                System.out.println(
+                        "Preborrow: "+(wt.preborrow-origin)
+                        + " Postborrow: "+(wt.postborrow != 0 ? wt.postborrow-origin : -1)
+                        + " BorrowTime: "+(wt.postborrow != 0 ? wt.postborrow-wt.preborrow : -1)
+                        + " PostReturn: "+(wt.postreturn != 0 ? wt.postreturn-origin : -1)
+                        + " Ended: "+(wt.ended-origin)
+                        + " ObjId: "+wt.objectId
+                        );
+            }            
+        }
+        assertEquals("Expected half the threads to fail",wtt.length/2,failed);
+    }
+    
+    /**
+     * Test the following scenario:
+     *   Thread 1 borrows an instance
+     *   Thread 2 starts to borrow another instance before thread 1 returns its instance
+     *   Thread 1 returns its instance while thread 2 is validating its newly created instance
+     * The test verifies that the instance created by Thread 2 is not leaked.
+     */
+    public void testMakeConcurrentWithReturn() throws Exception {
+        SimpleFactory factory = new SimpleFactory();
+        GenericObjectPool<String> pool = new GenericObjectPool<String>(factory); 
+        pool.setTestOnBorrow(true);
+        factory.setValid(true);
+        // Borrow and return an instance, with a short wait
+        WaitingTestThread<String> thread1 = new WaitingTestThread<String>(pool, 200);
+        thread1.start();
+        Thread.sleep(50); // wait for validation to succeed
+        // Slow down validation and borrow an instance
+        factory.setValidateLatency(400);
+        String instance = pool.borrowObject();
+        // Now make sure that we have not leaked an instance
+        assertEquals(factory.getMakeCounter(), pool.getNumIdle() + 1); 
+        pool.returnObject(instance);
+        assertEquals(factory.getMakeCounter(), pool.getNumIdle());
+    }
+    
+    public void testPoolTypeSubclass() throws Exception {
+        ObjectPool<Number> numberPool = new GenericObjectPool<Number>(new PoolableObjectFactory<Number>() {
+            public Number makeObject() throws Exception {
+                return BigInteger.ONE;
+            }
+
+            public void destroyObject(Number obj) throws Exception {
+                // do nothing                
+            }
+
+            public boolean validateObject(Number obj) {
+                return false;
+            }
+
+            public void activateObject(Number obj) throws Exception {
+                // do nothing                
+            }
+
+            public void passivateObject(Number obj) throws Exception {
+                // do nothing                
+            }
+        });
+        Number n = numberPool.borrowObject();
+        numberPool.returnObject(n);
+        BigInteger bi = (BigInteger) numberPool.borrowObject();
+        numberPool.returnObject(bi);
+    }
+}
diff --git a/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolFactory.java b/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolFactory.java
new file mode 100644
index 0000000..8a246fa
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolFactory.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.NoSuchElementException;
+
+import org.apache.commons.pool.MethodCallPoolableObjectFactory;
+import org.apache.commons.pool.ObjectPoolFactory;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.TestObjectPoolFactory;
+
+/**
+ * Tests for {@link GenericObjectPoolFactory}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestGenericObjectPoolFactory extends TestObjectPoolFactory {
+    public TestGenericObjectPoolFactory(final String name) {
+        super(name);
+    }
+
+    @Override
+    protected ObjectPoolFactory<Integer> makeFactory(final PoolableObjectFactory<Integer> objectFactory) throws UnsupportedOperationException {
+        return new GenericObjectPoolFactory<Integer>(objectFactory);
+    }
+
+    public void testConstructors() throws Exception {
+        GenericObjectPoolFactory<Integer> factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory());
+        GenericObjectPool<Integer> pool;
+        factory.createPool().close();
+
+        final GenericObjectPool.Config config = new GenericObjectPool.Config();
+        config.maxActive = 1;
+        config.maxIdle = 2;
+        config.maxWait = 3;
+        config.minIdle = 4;
+        config.minEvictableIdleTimeMillis = 5;
+        config.numTestsPerEvictionRun = 6;
+        config.softMinEvictableIdleTimeMillis = 7;
+        config.testOnBorrow = true;
+        config.testOnReturn = false;
+        config.testWhileIdle = true;
+        config.lifo = false;
+        config.timeBetweenEvictionRunsMillis = 8;
+        config.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), config);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxIdle());
+        assertEquals(3, pool.getMaxWait());
+        assertEquals(4, pool.getMinIdle());
+        assertEquals(5, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(6, pool.getNumTestsPerEvictionRun());
+        assertEquals(7, pool.getSoftMinEvictableIdleTimeMillis());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(true, pool.getTestWhileIdle());
+        assertEquals(false, pool.getLifo());
+        assertEquals(8, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_BLOCK, 125);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_BLOCK, pool.getWhenExhaustedAction());
+        assertEquals(125, pool.getMaxWait());
+        pool.borrowObject();
+        long startTime = System.currentTimeMillis();
+        try {
+            pool.borrowObject();
+            fail();
+        } catch (NoSuchElementException nsee) {
+            // expected
+        }
+        long delay = System.currentTimeMillis() - startTime;
+        assertTrue("delay: " + delay, delay > 100);
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_GROW, 2, true, false);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_GROW, 2, 3);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, true, false);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, true, false, 4, 5, 6, false);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(4, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(5, pool.getNumTestsPerEvictionRun());
+        assertEquals(6, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(false, pool.getTestWhileIdle());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, 4, true, false, 5, 6, 7, true);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(4, pool.getMinIdle());
+        assertEquals(5, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(6, pool.getNumTestsPerEvictionRun());
+        assertEquals(7, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(true, pool.getTestWhileIdle());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+
+
+        factory = new GenericObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1, GenericObjectPool.WHEN_EXHAUSTED_GROW, 2, 3, 4, true, false, 5, 6, 7, true, 8, false);
+        pool = (GenericObjectPool<Integer>)factory.createPool();
+        assertEquals(1, pool.getMaxActive());
+        assertEquals(2, pool.getMaxWait());
+        assertEquals(3, pool.getMaxIdle());
+        assertEquals(4, pool.getMinIdle());
+        assertEquals(5, pool.getTimeBetweenEvictionRunsMillis());
+        assertEquals(6, pool.getNumTestsPerEvictionRun());
+        assertEquals(7, pool.getMinEvictableIdleTimeMillis());
+        assertEquals(8, pool.getSoftMinEvictableIdleTimeMillis());
+        assertEquals(true, pool.getTestOnBorrow());
+        assertEquals(false, pool.getTestOnReturn());
+        assertEquals(true, pool.getTestWhileIdle());
+        assertEquals(false, pool.getLifo());
+        assertEquals(GenericObjectPool.WHEN_EXHAUSTED_GROW, pool.getWhenExhaustedAction());
+        pool.borrowObject();
+        pool.close();
+    }
+}
diff --git a/src/test/org/apache/commons/pool/impl/TestSoftRefOutOfMemory.java b/src/test/org/apache/commons/pool/impl/TestSoftRefOutOfMemory.java
new file mode 100644
index 0000000..662eff9
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestSoftRefOutOfMemory.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.pool.BasePoolableObjectFactory;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+/**
+ * @author Dirk Verbeeck
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestSoftRefOutOfMemory extends TestCase {
+    private SoftReferenceObjectPool<String> pool;
+
+    public TestSoftRefOutOfMemory(String testName) {
+        super(testName);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (pool != null) {
+            pool.close();
+            pool = null;
+        }
+        System.gc();
+    }
+
+    public void testOutOfMemory() throws Exception {
+        pool = new SoftReferenceObjectPool<String>(new SmallPoolableObjectFactory());
+
+        String obj = pool.borrowObject();
+        assertEquals("1", obj);
+        pool.returnObject(obj);
+        obj = null;
+
+        assertEquals(1, pool.getNumIdle());
+
+        final List<byte[]> garbage = new LinkedList<byte[]>();
+        final Runtime runtime = Runtime.getRuntime();
+        while (pool.getNumIdle() > 0) {
+            try {
+                long freeMemory = runtime.freeMemory();
+                if (freeMemory > Integer.MAX_VALUE) {
+                    freeMemory = Integer.MAX_VALUE;
+                }
+                garbage.add(new byte[Math.min(1024 * 1024, (int)freeMemory/2)]);
+            } catch (OutOfMemoryError oome) {
+                System.gc();
+            }
+            System.gc();
+        }
+        garbage.clear();
+        System.gc();
+
+        obj = pool.borrowObject();
+        assertEquals("2", obj);
+        pool.returnObject(obj);
+        obj = null;
+
+        assertEquals(1, pool.getNumIdle());
+    }
+
+    public void testOutOfMemory1000() throws Exception {
+        pool = new SoftReferenceObjectPool<String>(new SmallPoolableObjectFactory());
+
+        for (int i = 0 ; i < 1000 ; i++) {
+            pool.addObject();
+        }
+
+        String obj = pool.borrowObject();
+        assertEquals("1000", obj);
+        pool.returnObject(obj);
+        obj = null;
+
+        assertEquals(1000, pool.getNumIdle());
+
+        final List<byte[]> garbage = new LinkedList<byte[]>();
+        final Runtime runtime = Runtime.getRuntime();
+        while (pool.getNumIdle() > 0) {
+            try {
+                long freeMemory = runtime.freeMemory();
+                if (freeMemory > Integer.MAX_VALUE) {
+                    freeMemory = Integer.MAX_VALUE;
+                }
+                garbage.add(new byte[Math.min(1024 * 1024, (int)freeMemory/2)]);
+            } catch (OutOfMemoryError oome) {
+                System.gc();
+            }
+            System.gc();
+        }
+        garbage.clear();
+        System.gc();
+
+        obj = pool.borrowObject();
+        assertEquals("1001", obj);
+        pool.returnObject(obj);
+        obj = null;
+
+        assertEquals(1, pool.getNumIdle());
+    }
+
+    public void testOutOfMemoryLarge() throws Exception {
+        pool = new SoftReferenceObjectPool<String>(new LargePoolableObjectFactory(1000000));
+
+        String obj = pool.borrowObject();
+        assertTrue(obj.startsWith("1."));
+        pool.returnObject(obj);
+        obj = null;
+
+        assertEquals(1, pool.getNumIdle());
+
+        final List<byte[]> garbage = new LinkedList<byte[]>();
+        final Runtime runtime = Runtime.getRuntime();
+        while (pool.getNumIdle() > 0) {
+            try {
+                long freeMemory = runtime.freeMemory();
+                if (freeMemory > Integer.MAX_VALUE) {
+                    freeMemory = Integer.MAX_VALUE;
+                }
+                garbage.add(new byte[Math.min(1024 * 1024, (int)freeMemory/2)]);
+            } catch (OutOfMemoryError oome) {
+                System.gc();
+            }
+            System.gc();
+        }
+        garbage.clear();
+        System.gc();
+
+        obj = pool.borrowObject();
+        assertTrue(obj.startsWith("2."));
+        pool.returnObject(obj);
+        obj = null;
+
+        assertEquals(1, pool.getNumIdle());
+    }
+
+    /**
+     * Makes sure an {@link OutOfMemoryError} isn't swallowed.
+     */
+    public void testOutOfMemoryError() throws Exception {
+        pool = new SoftReferenceObjectPool<String>(new BasePoolableObjectFactory<String>() {
+            @Override
+            public String makeObject() throws Exception {
+                throw new OutOfMemoryError();
+            }
+        });
+
+        try {
+            pool.borrowObject();
+            fail("Expected out of memory.");
+        }
+        catch (OutOfMemoryError ex) {
+            // expected
+        }
+        pool.close();
+
+        pool = new SoftReferenceObjectPool<String>(new BasePoolableObjectFactory<String>() {
+            @Override
+            public String makeObject() throws Exception {
+                return new String();
+            }
+
+            @Override
+            public boolean validateObject(String obj) {
+                throw new OutOfMemoryError();
+            }
+        });
+
+        try {
+            pool.borrowObject();
+            fail("Expected out of memory.");
+        }
+        catch (OutOfMemoryError ex) {
+            // expected
+        }
+        pool.close();
+        
+        pool = new SoftReferenceObjectPool<String>(new BasePoolableObjectFactory<String>() {
+            @Override
+            public String makeObject() throws Exception {
+                return new String();
+            }
+
+            @Override
+            public boolean validateObject(String obj) {
+                throw new IllegalAccessError();
+            }
+
+            @Override
+            public void destroyObject(String obj) throws Exception {
+                throw new OutOfMemoryError();
+            }
+        });
+
+        try {
+            pool.borrowObject();
+            fail("Expected out of memory.");
+        }
+        catch (OutOfMemoryError ex) {
+            // expected
+        }
+        pool.close();
+
+    }
+
+
+    public static class SmallPoolableObjectFactory implements PoolableObjectFactory<String> {
+        private int counter = 0;
+
+        public String makeObject() {
+            counter++;
+            // It seems that as of Java 1.4 String.valueOf may return an
+            // intern()'ed String this may cause problems when the tests
+            // depend on the returned object to be eventually garbaged
+            // collected. Either way, making sure a new String instance
+            // is returned eliminated false failures.
+            return new String(String.valueOf(counter));
+        }
+        public boolean validateObject(String obj) {
+            return true;
+        }
+        public void activateObject(String obj) { }
+        public void passivateObject(String obj) { }
+        public void destroyObject(String obj) { }
+    }
+
+    public static class LargePoolableObjectFactory implements PoolableObjectFactory<String> {
+        private String buffer;
+        private int counter = 0;
+
+        public LargePoolableObjectFactory(int size) {
+            char[] data = new char[size];
+            Arrays.fill(data, '.');
+            buffer = new String(data);
+        }
+
+        public String makeObject() {
+            counter++;
+            return String.valueOf(counter) + buffer;
+        }
+        public boolean validateObject(String obj) {
+            return true;
+        }
+        public void activateObject(String obj) { }
+        public void passivateObject(String obj) { }
+        public void destroyObject(String obj) { }
+    }
+}
\ No newline at end of file
diff --git a/src/test/org/apache/commons/pool/impl/TestSoftReferenceObjectPool.java b/src/test/org/apache/commons/pool/impl/TestSoftReferenceObjectPool.java
new file mode 100644
index 0000000..b8ee047
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestSoftReferenceObjectPool.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.TestBaseObjectPool;
+
+/**
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestSoftReferenceObjectPool extends TestBaseObjectPool<String> {
+    public TestSoftReferenceObjectPool(String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected ObjectPool<String> makeEmptyPool(int cap) {
+        return new SoftReferenceObjectPool<String>(
+            new PoolableObjectFactory<String>()  {
+                int counter = 0;
+                public String makeObject() { return String.valueOf(counter++); }
+                public void destroyObject(String obj) { }
+                public boolean validateObject(String obj) { return true; }
+                public void activateObject(String obj) { }
+                public void passivateObject(String obj) { }
+            }
+            );
+    }
+
+    @Override
+    protected ObjectPool<Integer> makeEmptyPool(final PoolableObjectFactory<Integer> factory) {
+        return new SoftReferenceObjectPool<Integer>(factory);
+    }
+
+    @Override
+    protected String getNthObject(int n) {
+        return String.valueOf(n);
+    }
+
+    @Override
+    protected boolean isLifo() {
+        return false;
+    }
+
+    @Override
+    protected boolean isFifo() {
+        return false;
+    }
+
+}
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..961314e
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPool.java
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+import org.apache.commons.pool.TestBaseKeyedObjectPool;
+
+/**
+ * @author Rodney Waldhoff
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestStackKeyedObjectPool extends TestBaseKeyedObjectPool<String, String> {
+    public TestStackKeyedObjectPool(String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected KeyedObjectPool<String, String> makeEmptyPool(int mincapacity) {
+        StackKeyedObjectPool<String, String> pool = new StackKeyedObjectPool<String, String>(new SimpleFactory(),mincapacity);
+        return pool;
+    }
+
+    @Override
+    protected KeyedObjectPool<Object, Integer> makeEmptyPool(KeyedPoolableObjectFactory<Object, Integer> factory) {
+        return new StackKeyedObjectPool<Object, Integer>(factory);
+    }
+
+    @Override
+    protected String getNthObject(Object key, int n) {
+        return String.valueOf(key) + String.valueOf(n);
+    }
+
+    @Override
+    protected String makeKey(int n) {
+        return String.valueOf(n);
+    }
+
+    private StackKeyedObjectPool<String, String> pool = null;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        pool = new StackKeyedObjectPool<String, String>(
+            new KeyedPoolableObjectFactory<String, String>()  {
+                int counter = 0;
+                public String makeObject(String key) { return String.valueOf(key) + String.valueOf(counter++); }
+                public void destroyObject(String key, String obj) { }
+                public boolean validateObject(String key, String obj) { return true; }
+                public void activateObject(String key, String obj) { }
+                public void passivateObject(String key, String obj) { }
+            }
+            );
+    }
+
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        pool = null;
+    }
+
+    public void testCloseBug() throws Exception {
+        {
+            String obj0 = pool.borrowObject("");
+            String obj1 = pool.borrowObject("");
+            assertEquals(2,pool.getNumActive(""));
+            assertEquals(0,pool.getNumIdle(""));
+            pool.returnObject("",obj1);
+            pool.returnObject("",obj0);
+            assertEquals(0,pool.getNumActive(""));
+            assertEquals(2,pool.getNumIdle(""));
+        }
+        {
+            String obj0 = pool.borrowObject("2");
+            String obj1 = pool.borrowObject("2");
+            assertEquals(2,pool.getNumActive("2"));
+            assertEquals(0,pool.getNumIdle("2"));
+            pool.returnObject("2",obj1);
+            pool.returnObject("2",obj0);
+            assertEquals(0,pool.getNumActive("2"));
+            assertEquals(2,pool.getNumIdle("2"));
+        }
+        pool.close();
+    }
+
+    public void testIdleCap() throws Exception {
+        String[] active = new String[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject("");
+        }
+        assertEquals(100,pool.getNumActive(""));
+        assertEquals(0,pool.getNumIdle(""));
+        for(int i=0;i<100;i++) {
+            pool.returnObject("",active[i]);
+            assertEquals(99 - i,pool.getNumActive(""));
+            assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle(""));
+        }
+    }
+    
+    /**
+     * Verifies maxSleeping contract: When returnObject triggers maxSleeping exceeded,
+     * the bottom (oldest) instance in the pool is destroyed to make room for the newly
+     * returning instance, which is pushed onto the idle object stack.
+     */
+    public void testRemoveOldest() throws Exception {
+        pool._maxSleeping = 2;
+        String obj0 = pool.borrowObject("");
+        String obj1 = pool.borrowObject("");
+        String obj2 = pool.borrowObject("");
+        pool.returnObject("", obj0); // Push 0 onto bottom of stack
+        pool.returnObject("", obj1); // Push 1
+        pool.returnObject("", obj2); // maxSleeping exceeded -> 0 destroyed, 2 pushed
+        assertEquals("2", pool.borrowObject("")); // 2 was pushed on top
+        assertEquals("1", pool.borrowObject("")); // 1 still there
+        assertEquals("3", pool.borrowObject("")); // New instance created (0 is gone)
+    }
+
+    public void testPoolWithNullFactory() throws Exception {
+        KeyedObjectPool<String, Integer> pool = new StackKeyedObjectPool<String, Integer>(10);
+        for(int i=0;i<10;i++) {
+            pool.returnObject("X",new Integer(i));
+        }
+        for(int j=0;j<3;j++) {
+            Integer[] borrowed = new Integer[10];
+            BitSet found = new BitSet();
+            for(int i=0;i<10;i++) {
+                borrowed[i] = pool.borrowObject("X");
+                assertNotNull(borrowed);
+                assertTrue(!found.get(borrowed[i].intValue()));
+                found.set(borrowed[i].intValue());
+            }
+            for(int i=0;i<10;i++) {
+                pool.returnObject("X",borrowed[i]);
+            }
+        }
+        pool.invalidateObject("X",pool.borrowObject("X"));
+        pool.invalidateObject("X",pool.borrowObject("X"));
+        pool.clear("X");
+        pool.clear();
+    }
+
+    public void testVariousConstructors() throws Exception {
+        {
+            StackKeyedObjectPool<Object, Object> pool = new StackKeyedObjectPool<Object, Object>();
+            assertNotNull(pool);
+        }
+        {
+            StackKeyedObjectPool<Object, Object> pool = new StackKeyedObjectPool<Object, Object>(10);
+            assertNotNull(pool);
+        }
+        {
+            StackKeyedObjectPool<Object, Object> pool = new StackKeyedObjectPool<Object, Object>(10,5);
+            assertNotNull(pool);
+        }
+        {
+            StackKeyedObjectPool<Object, Object> pool = new StackKeyedObjectPool<Object, Object>(null);
+            assertNotNull(pool);
+        }
+        {
+            StackKeyedObjectPool<Object, Object> pool = new StackKeyedObjectPool<Object, Object>(null,10);
+            assertNotNull(pool);
+        }
+        {
+            StackKeyedObjectPool<Object, Object> pool = new StackKeyedObjectPool<Object, Object>(null,10,5);
+            assertNotNull(pool);
+        }
+    }
+
+    @Override
+    public void testToString() throws Exception {
+        StackKeyedObjectPool<String, String> pool = new StackKeyedObjectPool<String, String>(new SimpleFactory());
+        assertNotNull(pool.toString());
+        String obj = pool.borrowObject("key");
+        assertNotNull(pool.toString());
+        pool.returnObject("key",obj);
+        assertNotNull(pool.toString());
+    }
+
+    public void testBorrowFromEmptyPoolWithNullFactory() throws Exception {
+        KeyedObjectPool<String, Object> pool = new StackKeyedObjectPool<String, Object>();
+        try {
+            pool.borrowObject("x");
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    public void testSetFactory() throws Exception {
+        KeyedObjectPool<String, String> pool = new StackKeyedObjectPool<String, String>();
+        try {
+            pool.borrowObject("x");
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+        pool.setFactory(new SimpleFactory());
+        String obj = pool.borrowObject("x");
+        assertNotNull(obj);
+        pool.returnObject("x",obj);
+    }
+
+    public void testCantResetFactoryWithActiveObjects() throws Exception {
+        KeyedObjectPool<String, String> pool = new StackKeyedObjectPool<String, String>();
+        pool.setFactory(new SimpleFactory());
+        Object obj = pool.borrowObject("x");
+        assertNotNull(obj);
+
+        try {
+            pool.setFactory(new SimpleFactory());
+            fail("Expected IllegalStateException");
+        } catch(IllegalStateException e) {
+            // expected
+        }
+    }
+
+    public void testCanResetFactoryWithoutActiveObjects() throws Exception {
+        KeyedObjectPool<String, String> pool = new StackKeyedObjectPool<String, String>();
+        {
+            pool.setFactory(new SimpleFactory());
+            String obj = pool.borrowObject("x");
+            assertNotNull(obj);
+            pool.returnObject("x",obj);
+        }
+        {
+            pool.setFactory(new SimpleFactory());
+            String obj = pool.borrowObject("x");
+            assertNotNull(obj);
+            pool.returnObject("x",obj);
+        }
+    }
+
+    public void testBorrowReturnWithSometimesInvalidObjects() throws Exception {
+        KeyedObjectPool<String, Integer> pool = new StackKeyedObjectPool<String, Integer>(
+            new KeyedPoolableObjectFactory<String, Integer>() {
+                int counter = 0;
+                public Integer makeObject(String key) { return new Integer(counter++); }
+                public void destroyObject(String key, Integer obj) { }
+                public boolean validateObject(String key, Integer obj) {
+                    return ((obj.intValue() % 2) == 1);
+                }
+                public void activateObject(String key, Integer obj) { }
+                public void passivateObject(String key, Integer obj) {
+                    if((obj.intValue() % 3) == 0) {
+                        throw new RuntimeException("Couldn't passivate");
+                    }
+                }
+            }
+        );
+
+        Integer[] obj = new Integer[10];
+        for(int i=0;i<10;i++) {
+            Integer object = null;
+            int k = 0;
+            while (object == null && k < 100) { // bound not really needed
+                try {
+                    k++;
+                    object = pool.borrowObject("key");
+                    obj[i] = object;
+                } catch (NoSuchElementException ex) {
+                    // Expected for evens, which fail validation
+                }
+            }
+            assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
+        }
+        // 1,3,5,...,19 pass validation, get checked out
+        for(int i=0;i<10;i++) {
+            pool.returnObject("key",obj[i]);
+            assertEquals("Each time we borrow, get one less active.", 9-i, pool.getNumActive());
+        }
+        // 3, 9, 15 fail passivation.  
+        assertEquals(7,pool.getNumIdle());
+        assertEquals(new Integer(19), pool.borrowObject("key"));
+        assertEquals(new Integer(17), pool.borrowObject("key"));
+        assertEquals(new Integer(13), pool.borrowObject("key"));
+        assertEquals(new Integer(11), pool.borrowObject("key"));
+        assertEquals(new Integer(7), pool.borrowObject("key"));
+        assertEquals(new Integer(5), pool.borrowObject("key"));
+        assertEquals(new Integer(1), pool.borrowObject("key"));   
+    }
+
+    class SimpleFactory implements KeyedPoolableObjectFactory<String, String> {
+        HashMap<String, Integer> map = new HashMap<String, Integer>();
+        public String makeObject(String key) {
+            int counter = 0;
+            Integer Counter = map.get(key);
+            if(null != Counter) {
+                counter = Counter.intValue();
+            }
+            map.put(key,new Integer(counter + 1));
+            return String.valueOf(key) + String.valueOf(counter);
+        }
+        public void destroyObject(String key, String obj) { }
+        public boolean validateObject(String key, String obj) { return true; }
+        public void activateObject(String key, String obj) { }
+        public void passivateObject(String key, String obj) { }
+    }
+
+    @Override
+    protected boolean isLifo() {
+        return true;
+    }
+
+    @Override
+    protected boolean isFifo() {
+        return false;
+    }
+}
diff --git a/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPoolFactory.java b/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPoolFactory.java
new file mode 100644
index 0000000..f4c6de2
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestStackKeyedObjectPoolFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.KeyedObjectPoolFactory;
+import org.apache.commons.pool.KeyedPoolableObjectFactory;
+import org.apache.commons.pool.TestKeyedObjectPoolFactory;
+
+/**
+ * Tests for {@link StackKeyedObjectPoolFactory}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestStackKeyedObjectPoolFactory extends TestKeyedObjectPoolFactory {
+    public TestStackKeyedObjectPoolFactory(final String name) {
+        super(name);
+    }
+
+    @Override
+    protected KeyedObjectPoolFactory<Object, Integer> makeFactory(final KeyedPoolableObjectFactory<Object, Integer> objectFactory) throws UnsupportedOperationException {
+        return new StackKeyedObjectPoolFactory<Object, Integer>(objectFactory);
+    }
+
+    public void testConstructors() throws Exception {
+        StackKeyedObjectPoolFactory<Object, Integer> factory = new StackKeyedObjectPoolFactory<Object, Integer>();
+        factory.createPool().close();
+
+        factory = new StackKeyedObjectPoolFactory<Object, Integer>(1);
+        StackKeyedObjectPool<Object, Integer> pool = (StackKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1,pool._maxSleeping);
+        pool.close();
+
+        factory = new StackKeyedObjectPoolFactory<Object, Integer>(1, 2);
+        pool = (StackKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1,pool._maxSleeping);
+        assertEquals(2,pool._initSleepingCapacity);
+        pool.close();
+
+        factory = new StackKeyedObjectPoolFactory<Object, Integer>(createObjectFactory());
+        pool = (StackKeyedObjectPool<Object, Integer>)factory.createPool();
+        pool.close();
+
+        factory = new StackKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(),  1);
+        pool = (StackKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1,pool._maxSleeping);
+        pool.close();
+
+        factory = new StackKeyedObjectPoolFactory<Object, Integer>(createObjectFactory(),  1, 2);
+        pool = (StackKeyedObjectPool<Object, Integer>)factory.createPool();
+        assertEquals(1,pool._maxSleeping);
+        assertEquals(2,pool._initSleepingCapacity);
+        pool.close();
+
+    }
+}
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..1e31fc7
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestStackObjectPool.java
@@ -0,0 +1,634 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.TestBaseObjectPool;
+
+/**
+ * @author Rodney Waldhoff
+ * @author Dirk Verbeeck
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestStackObjectPool extends TestBaseObjectPool<String> {
+    public TestStackObjectPool(String testName) {
+        super(testName);
+    }
+
+    @Override
+    protected ObjectPool<String> makeEmptyPool(int mincap) {
+        return new StackObjectPool<String>(new SimpleFactory());
+    }
+
+    @Override
+    protected ObjectPool<Integer> makeEmptyPool(final PoolableObjectFactory<Integer> factory) {
+        return new StackObjectPool<Integer>(factory);
+    }
+
+    @Override
+    protected String getNthObject(int n) {
+        return String.valueOf(n);
+    }
+
+    public void testIdleCap() throws Exception {
+        ObjectPool<String> pool = makeEmptyPool(8);
+        String[] active = new String[100];
+        for(int i=0;i<100;i++) {
+            active[i] = pool.borrowObject();
+        }
+        assertEquals(100,pool.getNumActive());
+        assertEquals(0,pool.getNumIdle());
+        for(int i=0;i<100;i++) {
+            pool.returnObject(active[i]);
+            assertEquals(99 - i,pool.getNumActive());
+            assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle());
+        }
+    }
+
+    /**
+     * @deprecated - to be removed in pool 2.0
+     */
+    @Deprecated
+    public void testPoolWithNullFactory() throws Exception {
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(10);
+        for(int i=0;i<10;i++) {
+            pool.returnObject(new Integer(i));
+        }
+        for(int j=0;j<3;j++) {
+            Integer[] borrowed = new Integer[10];
+            BitSet found = new BitSet();
+            for(int i=0;i<10;i++) {
+                borrowed[i] = pool.borrowObject();
+                assertNotNull(borrowed);
+                assertTrue(!found.get(borrowed[i].intValue()));
+                found.set(borrowed[i].intValue());
+            }
+            for(int i=0;i<10;i++) {
+                pool.returnObject(borrowed[i]);
+            }
+        }
+        pool.invalidateObject(pool.borrowObject());
+        pool.invalidateObject(pool.borrowObject());
+        pool.clear();        
+    }
+    
+    /**
+     * @deprecated - to be removed in pool 2.0
+     */
+    @Deprecated
+    public void testBorrowFromEmptyPoolWithNullFactory() throws Exception {
+        ObjectPool<Object> pool = new StackObjectPool<Object>();
+        try {
+            pool.borrowObject();
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+    }
+    
+    /**
+     * @deprecated - to be removed in pool 2.0
+     */
+    @Deprecated
+    @Override
+    public void testSetFactory() throws Exception {
+        ObjectPool<String> pool = new StackObjectPool<String>();
+        try {
+            pool.borrowObject();
+            fail("Expected NoSuchElementException");
+        } catch(NoSuchElementException e) {
+            // expected
+        }
+        pool.setFactory(new SimpleFactory());
+        String obj = pool.borrowObject();
+        assertNotNull(obj);
+        pool.returnObject(obj);
+    }
+
+    /**
+     * @deprecated - to be removed in pool 2.0
+     */
+    @Deprecated
+    public void testCantResetFactoryWithActiveObjects() throws Exception {
+        ObjectPool<String> pool = new StackObjectPool<String>();
+        pool.setFactory(new SimpleFactory());
+        String obj = pool.borrowObject();
+        assertNotNull(obj);
+
+        try {
+            pool.setFactory(new SimpleFactory());
+            fail("Expected IllegalStateException");
+        } catch(IllegalStateException e) {
+            // expected
+        }        
+    }
+    
+    /**
+     * @deprecated - to be removed in pool 2.0
+     */
+    @Deprecated
+    public void testCanResetFactoryWithoutActiveObjects() throws Exception {
+        ObjectPool<String> pool = new StackObjectPool<String>();
+        {
+            pool.setFactory(new SimpleFactory());
+            String obj = pool.borrowObject();        
+            assertNotNull(obj);
+            pool.returnObject(obj);
+        }
+        {
+            pool.setFactory(new SimpleFactory());
+            String obj = pool.borrowObject();        
+            assertNotNull(obj);
+            pool.returnObject(obj);
+        }
+    }
+
+    /**
+     * Verifies that validation failures when borrowing newly created instances
+     * from the pool result in NoSuchElementExceptions and passivation failures
+     * result in instances not being returned to the pool.
+     */
+    public void testBorrowWithSometimesInvalidObjects() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        factory.setValidateSelectively(true);  // Even numbers fail validation
+        factory.setPassivateSelectively(true); // Multiples of 3 fail passivation
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 20);
+        Integer[] obj = new Integer[10];
+        for(int i=0;i<10;i++) {
+            Integer object = null;
+            int k = 0;
+            while (object == null && k < 100) { // bound not really needed
+                try {
+                    k++;
+                    object = pool.borrowObject();
+                    if (object.intValue() % 2 == 0) {
+                        fail("Expecting NoSuchElementException");
+                    } else {
+                        obj[i] = object; 
+                    }
+                } catch (NoSuchElementException ex) {
+                    // Should fail for evens
+                }
+            }
+            assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
+        }
+        // 1,3,5,...,19 pass validation, get checked out
+        for(int i=0;i<10;i++) {
+            pool.returnObject(obj[i]);
+            assertEquals("Each time we return, get one less active.", 9-i, pool.getNumActive());
+        }
+        // 3, 9, 15 fail passivation.  
+        assertEquals(7,pool.getNumIdle());
+        assertEquals(new Integer(19), pool.borrowObject());
+        assertEquals(new Integer(17), pool.borrowObject());
+        assertEquals(new Integer(13), pool.borrowObject());
+        assertEquals(new Integer(11), pool.borrowObject());
+        assertEquals(new Integer(7), pool.borrowObject());
+        assertEquals(new Integer(5), pool.borrowObject());
+        assertEquals(new Integer(1), pool.borrowObject());     
+    }
+    
+    /**
+     * Verifies that validation and passivation failures returning objects are handled
+     * properly - instances destroyed and not returned to the pool, but no exceptions propagated.
+     */
+    public void testBorrowReturnWithSometimesInvalidObjects() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 20);
+
+        Integer[] obj = new Integer[10];
+        for(int i=0;i<10;i++) {
+            obj[i] = pool.borrowObject();
+            assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
+            
+        }
+        
+        factory.setValidateSelectively(true);  // Even numbers fail validation
+        factory.setPassivateSelectively(true); // Multiples of 3 fail passivation
+
+        for(int i=0;i<10;i++) {
+            pool.returnObject(obj[i]);
+            assertEquals("Each time we return, get one less active.", 9-i, pool.getNumActive());
+        }
+        // 0,2,4,6,8 fail validation, 3, 9 fail passivation - 3 left.
+        assertEquals(3,pool.getNumIdle());
+    }
+     
+    public void testVariousConstructors() throws Exception {
+        {
+            StackObjectPool<Object> pool = new StackObjectPool<Object>();
+            assertNotNull(pool);
+        }
+        {
+            StackObjectPool<Object> pool = new StackObjectPool<Object>(10);
+            assertNotNull(pool);
+        }
+        {
+            StackObjectPool<Object> pool = new StackObjectPool<Object>(10,5);
+            assertNotNull(pool);
+        }
+        {
+            StackObjectPool<Object> pool = new StackObjectPool<Object>(null);
+            assertNotNull(pool);
+        }
+        {
+            StackObjectPool<Object> pool = new StackObjectPool<Object>(null,10);
+            assertNotNull(pool);
+        }
+        {
+            StackObjectPool<Object> pool = new StackObjectPool<Object>(null,10,5);
+            assertNotNull(pool);
+        }
+    }
+    
+    /**
+     * Verify that out of range constructor arguments are ignored.
+     */
+    public void testMaxIdleInitCapacityOutOfRange() throws Exception {
+        SimpleFactory factory = new SimpleFactory();
+        StackObjectPool<String> pool = new StackObjectPool<String>(factory, -1, 0);
+        assertEquals(pool.getMaxSleeping(), StackObjectPool.DEFAULT_MAX_SLEEPING);
+        pool.addObject();
+        pool.close();
+    }
+
+    /**
+     * Verifies that when returning objects cause maxSleeping exceeded, oldest instances
+     * are destroyed to make room for returning objects.
+     */
+    public void testReturnObjectDiscardOrder() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 3);
+
+        // borrow more objects than the pool can hold
+        Integer i0 = pool.borrowObject();
+        Integer i1 = pool.borrowObject();
+        Integer i2 = pool.borrowObject();
+        Integer i3 = pool.borrowObject();
+
+        // tests
+        // return as many as the pool will hold.
+        pool.returnObject(i0);
+        pool.returnObject(i1);
+        pool.returnObject(i2);
+
+        // the pool should now be full.
+        assertEquals("No returned objects should have been destroyed yet.", 0,  factory.getDestroyed().size());
+
+        // cause the pool to discard a stale object.
+        pool.returnObject(i3);
+        assertEquals("One object should have been destroyed.", 1, factory.getDestroyed().size());
+
+        // check to see what object was destroyed
+        Integer d = factory.getDestroyed().get(0);
+        assertEquals("Destoryed object should be the stalest object.", i0, d);
+    }
+    
+    /**
+     * Verifies that exceptions thrown by factory activate method are not propagated to
+     * the caller.  Objects that throw on activate are destroyed and if none succeed,
+     * the caller gets NoSuchElementException.
+     */
+    public void testExceptionOnActivate() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
+        pool.addObject();
+        pool.addObject();
+        factory.setThrowOnActivate(true);
+        try {
+            pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // expected
+        }
+        assertEquals(0, pool.getNumIdle());
+        assertEquals(0, pool.getNumActive());
+    }
+    
+    /**
+     * Verifies that exceptions thrown by factory destroy are swallowed
+     * by both addObject and returnObject.
+     */
+    public void testExceptionOnDestroy() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 2);
+        factory.setThrowOnDestroy(true);
+        for (int i = 0; i < 3; i++) {
+            pool.addObject(); // Third one will destroy, exception should be swallowed
+        }
+        assertEquals(2, pool.getNumIdle());
+        
+        Integer[] objects = new Integer[3];
+        for (int i = 0; i < 3; i++) {
+            objects[i] = pool.borrowObject();
+        }
+        for (int i = 0; i < 3; i++) {
+            pool.returnObject(objects[i]); // Third triggers destroy
+        } 
+        assertEquals(2, pool.getNumIdle());
+    }
+    
+    /**
+     * Verifies that addObject propagates exceptions thrown by
+     * factory passivate, but returnObject swallows these.
+     */
+    public void testExceptionOnPassivate() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 2);
+        factory.setThrowOnPassivate(true);
+        
+        // addObject propagates
+        try {
+            pool.addObject();
+            fail("Expecting IntegerFactoryException");
+        } catch (IntegerFactoryException ex) {
+            assertEquals("passivateObject", ex.getType());
+            assertEquals(0, ex.getValue());
+        }
+        assertEquals(0, pool.getNumIdle());
+        
+        // returnObject swallows 
+        Integer obj = pool.borrowObject();
+        pool.returnObject(obj);
+        assertEquals(0, pool.getNumIdle());
+    }
+    
+    /**
+     * Verifies that validation exceptions always propagate
+     */
+    public void testExceptionOnValidate() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 2);
+        factory.setThrowOnValidate(true);
+        
+        // addObject
+        try {
+            pool.addObject();
+            fail("Expecting IntegerFactoryException");
+        } catch (IntegerFactoryException ex) {
+            assertEquals("validateObject", ex.getType());
+        }
+        assertEquals(0, pool.getNumIdle());
+        
+        // returnObject 
+        factory.setThrowOnValidate(false);
+        Integer obj = pool.borrowObject();
+        factory.setThrowOnValidate(true);
+        try {
+            pool.returnObject(obj);
+            fail("Expecting IntegerFactoryException");
+        } catch (IntegerFactoryException ex) {
+            assertEquals("validateObject", ex.getType());
+        }
+        assertEquals(0, pool.getNumIdle());
+        
+        // borrowObject - throws NoSuchElementException
+        try {
+            pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // Expected
+        }
+    }
+    
+    /**
+     * Verifies that exceptions thrown by makeObject are propagated.
+     */
+    public void testExceptionOnMake() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        factory.setThrowOnMake(true);
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
+        try {
+            pool.borrowObject();
+            fail("Expecting IntegerFactoryException");
+        } catch (IntegerFactoryException ex) {
+            assertEquals("makeObject", ex.getType());
+        }
+        try {
+            pool.addObject();
+            fail("Expecting IntegerFactoryException");
+        } catch (IntegerFactoryException ex) {
+            assertEquals("makeObject", ex.getType());
+        }
+    }
+    
+    /**
+     * Verifies NoSuchElementException when the factory returns a null object in borrowObject
+     */
+    public void testMakeNull() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
+        factory.setMakeNull(true);
+        try {
+            pool.borrowObject();
+            fail("Expecting NoSuchElementException");
+        } catch (NoSuchElementException ex) {
+            // Expected
+        }
+    }
+    
+    /**
+     * Verifies that initIdleCapacity is not a hard limit, but maxIdle is.
+     */
+    public void testInitIdleCapacityExceeded() throws Exception {
+        PoolableObjectFactory<String> factory = new SimpleFactory();
+        ObjectPool<String> pool = new StackObjectPool<String>(factory, 2, 1);
+        pool.addObject();
+        pool.addObject();
+        assertEquals(2, pool.getNumIdle());
+        pool.close();
+        pool = new StackObjectPool<String>(factory, 1, 2);
+        pool.addObject();
+        pool.addObject();
+        assertEquals(1, pool.getNumIdle());
+    }
+    
+    /**
+     * Verifies close contract - idle instances are destroyed, returning instances
+     * are destroyed, add/borrowObject throw IllegalStateException.
+     */
+    @Override
+    public void testClose() throws Exception {
+        SelectiveFactory factory = new SelectiveFactory();
+        ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
+        pool.addObject(); // 0
+        pool.addObject(); // 1
+        pool.addObject(); // 2
+        Integer two = pool.borrowObject();
+        assertEquals(2, two.intValue());
+        pool.close();
+        assertEquals(0, pool.getNumIdle());
+        assertEquals(1, pool.getNumActive());
+        List<Integer> destroyed = factory.getDestroyed();
+        assertEquals(2, destroyed.size());
+        assertTrue(destroyed.contains(new Integer(0)));
+        assertTrue(destroyed.contains(new Integer(0)));
+        pool.returnObject(two);
+        assertTrue(destroyed.contains(two));
+        try {
+            pool.addObject();
+            fail("Expecting IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // Expected
+        }
+        try {
+            pool.borrowObject();
+            fail("Expecting IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // Expected
+        }
+    }
+
+    /**
+     * Simple factory that creates Integers. Validation and other factory methods
+     * always succeed.
+     */
+    static class SimpleFactory implements PoolableObjectFactory<String> {
+        int counter = 0;
+        public String makeObject() { return String.valueOf(counter++); }
+        public void destroyObject(String obj) { }
+        public boolean validateObject(String obj) { return true; }
+        public void activateObject(String obj) { }
+        public void passivateObject(String obj) { }
+    }
+    
+    /**
+     * Integer factory that fails validation and other factory methods "selectively" and
+     * tracks object destruction.
+     */
+    static class SelectiveFactory implements PoolableObjectFactory<Integer> {
+        private List<Integer> destroyed = new ArrayList<Integer>();
+        private int counter = 0;
+        private boolean validateSelectively = false;  // true <-> validate returns false for even Integers
+        private boolean passivateSelectively = false; // true <-> passivate throws RTE if Integer = 0 mod 3
+        private boolean throwOnDestroy = false;       // true <-> destroy throws RTE (always)
+        private boolean throwOnActivate = false;      // true <-> activate throws RTE (always)
+        private boolean throwOnMake = false;          // true <-> make throws RTE (always)
+        private boolean throwOnValidate= false;       // true <-> validate throws RTE (always)
+        private boolean throwOnPassivate = false;     // true <-> passivate throws RTE (always)
+        private boolean makeNull = false;             // true <-> make returns null
+        public Integer makeObject() {
+            if (throwOnMake) {
+                final int next = counter + 1;
+                throw new IntegerFactoryException("makeObject", next);
+            } else {
+                return makeNull? null : new Integer(counter++);
+            }
+        }
+        public void destroyObject(Integer obj) {
+            if (throwOnDestroy) {
+                final Integer integer = obj;
+                throw new IntegerFactoryException("destroyObject", integer.intValue());
+            }
+            destroyed.add(obj);
+        }
+        public boolean validateObject(Integer obj) {
+            if (throwOnValidate) {
+                final Integer integer = obj;
+                throw new IntegerFactoryException("validateObject", integer.intValue());
+            }
+            if (validateSelectively) {
+                // only odd objects are valid
+                return ((obj.intValue() % 2) == 1);
+            }
+            return true;
+        }
+        public void activateObject(Integer obj) {
+            if (throwOnActivate) {
+                final Integer integer = obj;
+                throw new IntegerFactoryException("activateObject", integer.intValue());
+            }
+        }
+        public void passivateObject(Integer obj) { 
+            if (throwOnPassivate) {
+                final Integer integer = obj;
+                throw new IntegerFactoryException("passivateObject", integer.intValue());
+            }
+            if (passivateSelectively) {
+                final Integer integer = obj;
+                if (integer.intValue() % 3 == 0) {
+                    throw new IntegerFactoryException("passivateObject", integer.intValue());
+                }
+            }
+        }
+        public List<Integer> getDestroyed() {
+            return destroyed;
+        }
+        public void setCounter(int counter) {
+            this.counter = counter;
+        }
+        public void setValidateSelectively(boolean validateSelectively) {
+            this.validateSelectively = validateSelectively;
+        }
+        public void setPassivateSelectively(boolean passivateSelectively) {
+            this.passivateSelectively = passivateSelectively;
+        }
+        public void setThrowOnDestroy(boolean throwOnDestroy) {
+            this.throwOnDestroy = throwOnDestroy;
+        }
+        public void setThrowOnActivate(boolean throwOnActivate) {
+            this.throwOnActivate = throwOnActivate;
+        }
+        public void setThrowOnMake(boolean throwOnMake) {
+            this.throwOnMake = throwOnMake;
+        }
+        public void setThrowOnPassivate(boolean throwOnPassivate) {
+            this.throwOnPassivate = throwOnPassivate;
+        }
+        public void setThrowOnValidate(boolean throwOnValidate) {
+            this.throwOnValidate = throwOnValidate;
+        }
+        public void setMakeNull(boolean makeNull) {
+            this.makeNull = makeNull;
+        }
+    }
+    
+    static class IntegerFactoryException extends RuntimeException {
+        private String type;
+        private int value;
+        public IntegerFactoryException(String type, int value) {
+            super(type + " failed. Value: " + value);
+            this.type = type;
+            this.value = value;
+        }
+        public String getType() {
+            return type;
+        }
+        public int getValue() {
+            return value;
+        }
+    }
+
+    @Override
+    protected boolean isLifo() {
+        return true;
+    }
+
+    @Override
+    protected boolean isFifo() {
+        return false;
+    }
+}
+
diff --git a/src/test/org/apache/commons/pool/impl/TestStackObjectPoolFactory.java b/src/test/org/apache/commons/pool/impl/TestStackObjectPoolFactory.java
new file mode 100644
index 0000000..376d648
--- /dev/null
+++ b/src/test/org/apache/commons/pool/impl/TestStackObjectPoolFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.impl;
+
+import org.apache.commons.pool.MethodCallPoolableObjectFactory;
+import org.apache.commons.pool.ObjectPoolFactory;
+import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.commons.pool.TestObjectPoolFactory;
+
+/**
+ * Tests for {@link StackObjectPoolFactory}.
+ *
+ * @author Sandy McArthur
+ * @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
+ */
+public class TestStackObjectPoolFactory extends TestObjectPoolFactory {
+    public TestStackObjectPoolFactory(final String name) {
+        super(name);
+    }
+
+    @Override
+    protected ObjectPoolFactory<Integer> makeFactory(final PoolableObjectFactory<Integer> objectFactory) throws UnsupportedOperationException {
+        return new StackObjectPoolFactory<Integer>(objectFactory);
+    }
+
+    public void testConstructors() throws Exception {
+        StackObjectPoolFactory<Integer> factory = new StackObjectPoolFactory<Integer>();
+        factory.createPool().close();
+
+        
+        factory = new StackObjectPoolFactory<Integer>(1);
+        StackObjectPool<Integer> pool = (StackObjectPool<Integer>)factory.createPool();
+        pool.close();
+
+
+        factory = new StackObjectPoolFactory<Integer>(1, 1);
+        pool = (StackObjectPool<Integer>)factory.createPool();
+        pool.close();
+
+
+        factory = new StackObjectPoolFactory<Integer>(new MethodCallPoolableObjectFactory(), 1);
+        pool = (StackObjectPool<Integer>)factory.createPool();
+        Integer a = pool.borrowObject();
+        Integer b = pool.borrowObject();
+        pool.returnObject(a);
+        pool.returnObject(b);
+        assertEquals(1, pool.getNumIdle());
+        pool.close();
+    }
+}
diff --git a/src/test/org/apache/commons/pool/performance/PerformanceTest.java b/src/test/org/apache/commons/pool/performance/PerformanceTest.java
new file mode 100644
index 0000000..6e5a4fb
--- /dev/null
+++ b/src/test/org/apache/commons/pool/performance/PerformanceTest.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.performance;
+
+import org.apache.commons.pool.impl.GenericObjectPool;
+
+/**
+ * Multi-thread performance test
+ * 
+ * @author Dirk Verbeeck
+ * @version $Revision: 1221705 $ $Date: 2011-12-21 08:03:54 -0500 (Wed, 21 Dec 2011) $ 
+ */
+public class PerformanceTest {
+    private int logLevel = 0;
+    private int nrIterations = 5;
+
+    private GenericObjectPool<Integer> pool;
+    private boolean start = false;
+    private volatile int waiting = 0;
+    private volatile int complete = 0;
+    private volatile long totalBorrowTime = 0;
+    private volatile long totalReturnTime = 0;
+    private volatile int nrSamples = 0; 
+
+    public void setLogLevel(int i) {
+        logLevel = i;
+    }
+    
+    private void init() {
+        start = false;
+        waiting = 0;
+        complete = 0;
+        totalBorrowTime = 0;
+        totalReturnTime = 0;
+        nrSamples = 0;     
+    }
+
+    class MyThread implements Runnable {
+        long borrowTime;
+        long returnTime;
+
+        public void runOnce() {
+            try {
+                waiting++;
+                if (logLevel >= 5) {
+                    String name = "thread" + Thread.currentThread().getName();
+                    System.out.println(name + "   waiting: " + waiting + "   complete: " + complete);
+                }
+                long bbegin = System.currentTimeMillis();
+                Integer o = pool.borrowObject();
+                long bend = System.currentTimeMillis();
+                waiting--;
+                do {
+                    Thread.yield();
+                }
+                while (!start);
+
+                if (logLevel >= 3) {
+                    String name = "thread" + Thread.currentThread().getName();
+                    System.out.println(name + "    waiting: " + waiting + "   complete: " + complete);
+                }
+                                 
+                long rbegin = System.currentTimeMillis();
+                pool.returnObject(o);
+                long rend = System.currentTimeMillis();
+                Thread.yield();
+                complete++;
+                borrowTime = (bend-bbegin);
+                returnTime = (rend-rbegin);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void run() {
+            runOnce(); // warmup
+            for (int i = 0; i<nrIterations; i++) {
+                runOnce();
+                totalBorrowTime += borrowTime;
+                totalReturnTime += returnTime;
+                nrSamples++;
+                if (logLevel >= 2) {
+                    String name = "thread" + Thread.currentThread().getName();
+                    System.out.println(
+                        "result " + nrSamples + "\t" + name 
+                        + "\t" + "borrow time: " + borrowTime + "\t" + "return time: " + returnTime
+                        + "\t" + "waiting: " + waiting + "\t" + "complete: " + complete);
+                }
+            }
+        }
+    }
+
+    private void run(int nrIterations, int nrThreads, int maxActive, int maxIdle) {
+        this.nrIterations = nrIterations;
+        init();
+        
+        SleepingObjectFactory factory = new SleepingObjectFactory();
+        if (logLevel >= 4) { factory.setDebug(true); } 
+        pool = new GenericObjectPool<Integer>(factory);
+        pool.setMaxActive(maxActive);
+        pool.setMaxIdle(maxIdle);
+        pool.setTestOnBorrow(true);
+
+        Thread[] threads = new Thread[nrThreads];
+        for (int i = 0; i < threads.length; i++) {
+            threads[i]= new Thread(new MyThread(), Integer.toString(i));
+            Thread.yield();
+        }
+        if (logLevel >= 1) { System.out.println("created"); } 
+        Thread.yield();
+
+        for (int i = 0; i < threads.length; i++) {
+            threads[i].start();
+            Thread.yield();
+        }
+        if (logLevel >= 1) { System.out.println("started"); }
+        Thread.yield();
+
+        start = true;
+        if (logLevel >= 1) { System.out.println("go"); }
+        Thread.yield();
+
+        for (int i = 0; i < threads.length; i++) {
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        if (logLevel >= 1) { System.out.println("finish"); }
+        System.out.println("-----------------------------------------");
+        System.out.println("nrIterations: " + nrIterations);
+        System.out.println("nrThreads: " + nrThreads);
+        System.out.println("maxActive: " + maxActive);
+        System.out.println("maxIdle: " + maxIdle);
+        System.out.println("nrSamples: " + nrSamples);
+        System.out.println("totalBorrowTime: " + totalBorrowTime);
+        System.out.println("totalReturnTime: " + totalReturnTime);
+        System.out.println("avg BorrowTime: " + totalBorrowTime/nrSamples);
+        System.out.println("avg ReturnTime: " + totalReturnTime/nrSamples);
+    }
+
+    public static void main(String[] args) {
+        PerformanceTest test = new PerformanceTest();
+        test.setLogLevel(0);
+        System.out.println("Increase threads");
+        test.run(1,  50,  5,  5);
+        test.run(1, 100,  5,  5);
+        test.run(1, 200,  5,  5);
+        test.run(1, 400,  5,  5);
+
+        System.out.println("Increase threads & poolsize");
+        test.run(1,  50,  5,  5);
+        test.run(1, 100, 10, 10);
+        test.run(1, 200, 20, 20);
+        test.run(1, 400, 40, 40);
+
+        System.out.println("Increase maxIdle");
+        test.run(1, 400, 40,  5);
+        test.run(1, 400, 40, 40);
+
+
+//      System.out.println("Show creation/destruction of objects");
+//      test.setLogLevel(4);
+//      test.run(1, 400, 40,  5);
+    }
+
+}
diff --git a/src/test/org/apache/commons/pool/performance/SleepingObjectFactory.java b/src/test/org/apache/commons/pool/performance/SleepingObjectFactory.java
new file mode 100644
index 0000000..2ab9dc2
--- /dev/null
+++ b/src/test/org/apache/commons/pool/performance/SleepingObjectFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool.performance;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+
+/**
+ * Sleepy ObjectFactory (everything takes a while longer)
+ * 
+ * @author Dirk Verbeeck
+ * @version $Revision: 1221705 $ $Date: 2011-12-21 08:03:54 -0500 (Wed, 21 Dec 2011) $ 
+ */
+public class SleepingObjectFactory implements PoolableObjectFactory<Integer> {
+
+    private int counter = 0;
+    private boolean debug = false;
+
+    public Integer makeObject() throws Exception {
+        Integer obj = new Integer(counter++);
+        debug("makeObject", obj);
+        sleep(500);
+        return obj;
+    }
+
+    public void destroyObject(Integer obj) throws Exception {
+        debug("destroyObject", obj);
+        sleep(250);
+    }
+
+    public boolean validateObject(Integer obj) {
+        debug("validateObject", obj);
+        sleep(30);
+        return true;
+    }
+
+    public void activateObject(Integer obj) throws Exception {
+        debug("activateObject", obj);
+        sleep(10);
+    }
+
+    public void passivateObject(Integer obj) throws Exception {
+        debug("passivateObject", obj);
+        sleep(10);
+    }
+    
+    private void debug(String method, Object obj) {
+        if (debug) {
+            String thread = "thread" + Thread.currentThread().getName();
+            System.out.println(thread + ": " + method + " " + obj);
+        }
+    }
+    
+    private void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    public boolean isDebug() {
+        return debug;
+    }
+
+    public void setDebug(boolean b) {
+        debug = b;
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/commons-pool.git



More information about the pkg-java-commits mailing list