This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 0d44c6a9928e0dc07605763c689e0bb9361e9acf
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Dec 12 11:18:35 2025 +0000

    Update the internal fork of Commons Pool to 2.13.0.
---
 java/org/apache/tomcat/dbcp/pool2/BaseObject.java  |   9 +-
 .../apache/tomcat/dbcp/pool2/BaseObjectPool.java   |   9 +-
 java/org/apache/tomcat/dbcp/pool2/DestroyMode.java |   2 +-
 .../apache/tomcat/dbcp/pool2/KeyedObjectPool.java  |   2 +-
 .../dbcp/pool2/KeyedPooledObjectFactory.java       |   2 +-
 java/org/apache/tomcat/dbcp/pool2/ObjectPool.java  |   2 +-
 java/org/apache/tomcat/dbcp/pool2/PoolUtils.java   |  48 ++--
 .../org/apache/tomcat/dbcp/pool2/PooledObject.java |  33 ++-
 .../tomcat/dbcp/pool2/PooledObjectFactory.java     |   2 +-
 .../tomcat/dbcp/pool2/PooledObjectState.java       |   2 +-
 .../dbcp/pool2/SwallowedExceptionListener.java     |   2 +-
 java/org/apache/tomcat/dbcp/pool2/TrackedUse.java  |   2 +-
 .../apache/tomcat/dbcp/pool2/UsageTracking.java    |   2 +-
 .../tomcat/dbcp/pool2/impl/AbandonedConfig.java    |   2 +-
 .../dbcp/pool2/impl/BaseGenericObjectPool.java     | 125 ++++++++---
 .../dbcp/pool2/impl/BaseObjectPoolConfig.java      |  62 +++++-
 .../apache/tomcat/dbcp/pool2/impl/CallStack.java   |   2 +-
 .../dbcp/pool2/impl/DefaultEvictionPolicy.java     |   9 +-
 .../dbcp/pool2/impl/DefaultPooledObject.java       |   2 +-
 .../dbcp/pool2/impl/DefaultPooledObjectInfo.java   |   2 +-
 .../pool2/impl/DefaultPooledObjectInfoMBean.java   |   2 +-
 .../tomcat/dbcp/pool2/impl/EvictionConfig.java     |   2 +-
 .../tomcat/dbcp/pool2/impl/EvictionPolicy.java     |   4 +-
 .../tomcat/dbcp/pool2/impl/EvictionTimer.java      |  25 ++-
 .../dbcp/pool2/impl/GenericKeyedObjectPool.java    | 248 +++++++++++++++++----
 .../pool2/impl/GenericKeyedObjectPoolConfig.java   |  82 ++++++-
 .../pool2/impl/GenericKeyedObjectPoolMXBean.java   |   2 +-
 .../tomcat/dbcp/pool2/impl/GenericObjectPool.java  | 229 +++++++++----------
 .../dbcp/pool2/impl/GenericObjectPoolConfig.java   |   9 +-
 .../dbcp/pool2/impl/GenericObjectPoolMXBean.java   |   2 +-
 .../pool2/impl/InterruptibleReentrantLock.java     |   8 +-
 .../dbcp/pool2/impl/LinkedBlockingDeque.java       |  66 ++++--
 .../tomcat/dbcp/pool2/impl/NoOpCallStack.java      |   4 +-
 .../tomcat/dbcp/pool2/impl/PoolImplUtils.java      |   2 +-
 .../dbcp/pool2/impl/PooledSoftReference.java       |   2 +-
 .../dbcp/pool2/impl/SoftReferenceObjectPool.java   |   8 +-
 .../tomcat/dbcp/pool2/impl/ThrowableCallStack.java |   4 +-
 res/checkstyle/header-al2.txt                      |   2 +-
 webapps/docs/changelog.xml                         |   3 +
 39 files changed, 745 insertions(+), 280 deletions(-)

diff --git a/java/org/apache/tomcat/dbcp/pool2/BaseObject.java 
b/java/org/apache/tomcat/dbcp/pool2/BaseObject.java
index 87c6cce6a3..47e2fe1b68 100644
--- a/java/org/apache/tomcat/dbcp/pool2/BaseObject.java
+++ b/java/org/apache/tomcat/dbcp/pool2/BaseObject.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -23,6 +23,13 @@ package org.apache.tomcat.dbcp.pool2;
  */
 public abstract class BaseObject {
 
+    /**
+     * Constructs a new instance.
+     */
+    public BaseObject() {
+        // empty
+    }
+
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
diff --git a/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java
index 94ed157997..dbca4addf7 100644
--- a/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -32,6 +32,13 @@ public abstract class BaseObjectPool<T> extends BaseObject 
implements ObjectPool
 
     private volatile boolean closed;
 
+    /**
+     * Constructs a new instance.
+     */
+    public BaseObjectPool() {
+        // empty
+    }
+
     /**
      * Not supported in this base implementation. Subclasses should override
      * this behavior.
diff --git a/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java 
b/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java
index 2b8b6833a0..bc073cb19b 100644
--- a/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java
+++ b/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java
index 6d1ba97f45..c9ed7da6a8 100644
--- a/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java 
b/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java
index 9c0a71c5a9..d93008dcfa 100644
--- a/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java
+++ b/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java
index 848957f0f0..b93a19cd52 100644
--- a/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java 
b/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java
index be6b52d374..4109abb147 100644
--- a/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java
+++ b/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -46,6 +47,9 @@ public final class PoolUtils {
      * frequently.
      */
     private static final class ErodingFactor {
+
+        private static final float MAX_INTERVAL = 15f;
+
         /** Determines frequency of "erosion" events */
         private final float factor;
 
@@ -53,7 +57,9 @@ public final class PoolUtils {
         private transient volatile long nextShrinkMillis;
 
         /** High water mark - largest numIdle encountered */
-        private transient volatile int idleHighWaterMark;
+        private transient volatile int idleHighWaterMark = 1;
+
+        private final ReentrantLock lock = new ReentrantLock();
 
         /**
          * Creates a new ErodingFactor with the given erosion factor.
@@ -61,10 +67,9 @@ public final class PoolUtils {
          * @param factor
          *            erosion factor
          */
-        ErodingFactor(final float factor) {
+        private ErodingFactor(final float factor) {
             this.factor = factor;
-            nextShrinkMillis = System.currentTimeMillis() + (long) (900000 * 
factor); // now + 15 min * factor
-            idleHighWaterMark = 1;
+            nextShrinkMillis = System.currentTimeMillis() + (long) (900_000 * 
factor); // now + 15 min * factor
         }
 
         /**
@@ -72,7 +77,7 @@ public final class PoolUtils {
          *
          * @return next shrink time
          */
-        public long getNextShrink() {
+        private long getNextShrink() {
             return nextShrinkMillis;
         }
 
@@ -81,7 +86,7 @@ public final class PoolUtils {
          */
         @Override
         public String toString() {
-            return "ErodingFactor{" + "factor=" + factor +
+            return "ErodingFactor{factor=" + factor +
                     ", idleHighWaterMark=" + idleHighWaterMark + '}';
         }
 
@@ -95,11 +100,14 @@ public final class PoolUtils {
          */
         public void update(final long nowMillis, 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;
-            nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor);
+            lock.lock();
+            try {
+                idleHighWaterMark = Math.max(idle, idleHighWaterMark);
+                final float minutes = MAX_INTERVAL + (1f - MAX_INTERVAL) / 
idleHighWaterMark * idle;
+                nextShrinkMillis = nowMillis + (long) (minutes * 60000f * 
factor);
+            } finally {
+                lock.unlock();
+            }
         }
     }
     /**
@@ -129,7 +137,7 @@ public final class PoolUtils {
          *            events
          * @see #erodingFactor
          */
-        protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
+        private ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
                 final ErodingFactor erodingFactor) {
             if (keyedPool == null) {
                 throw new IllegalArgumentException(
@@ -150,7 +158,7 @@ public final class PoolUtils {
          *            events
          * @see #erodingFactor
          */
-        ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
+        private ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool,
                 final float factor) {
             this(keyedPool, new ErodingFactor(factor));
         }
@@ -315,7 +323,7 @@ public final class PoolUtils {
          */
         @Override
         public String toString() {
-            return "ErodingKeyedObjectPool{" + "factor=" +
+            return "ErodingKeyedObjectPool{factor=" +
                     erodingFactor + ", keyedPool=" + keyedPool + '}';
         }
     }
@@ -346,7 +354,7 @@ public final class PoolUtils {
          *            events
          * @see #factor
          */
-        ErodingObjectPool(final ObjectPool<T> pool, final float factor) {
+        private ErodingObjectPool(final ObjectPool<T> pool, final float 
factor) {
             this.pool = pool;
             this.factor = new ErodingFactor(factor);
         }
@@ -355,7 +363,7 @@ public final class PoolUtils {
          * {@inheritDoc}
          */
         @Override
-        public void addObject() throws Exception{
+        public void addObject() throws Exception {
             pool.addObject();
         }
 
@@ -457,7 +465,7 @@ public final class PoolUtils {
          */
         @Override
         public String toString() {
-            return "ErodingObjectPool{" + "factor=" + factor + ", pool=" +
+            return "ErodingObjectPool{factor=" + factor + ", pool=" +
                     pool + '}';
         }
     }
@@ -486,7 +494,7 @@ public final class PoolUtils {
          * @param factor
          *            erosion factor
          */
-        ErodingPerKeyKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, 
final float factor) {
+        private ErodingPerKeyKeyedObjectPool(final KeyedObjectPool<K, V> 
keyedPool, final float factor) {
             super(keyedPool, null);
             this.factor = factor;
         }
@@ -506,7 +514,7 @@ public final class PoolUtils {
          */
         @Override
         public String toString() {
-            return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor +
+            return "ErodingPerKeyKeyedObjectPool{factor=" + factor +
                     ", keyedPool=" + getKeyedPool() + '}';
         }
     }
diff --git a/java/org/apache/tomcat/dbcp/pool2/PooledObject.java 
b/java/org/apache/tomcat/dbcp/pool2/PooledObject.java
index 75bbc941ad..65497d5e9d 100644
--- a/java/org/apache/tomcat/dbcp/pool2/PooledObject.java
+++ b/java/org/apache/tomcat/dbcp/pool2/PooledObject.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -34,14 +34,37 @@ import java.util.Deque;
 public interface PooledObject<T> extends Comparable<PooledObject<T>> {
 
     /**
-     * Tests whether the given PooledObject is null <em>or</em> contains a 
null.
+     * Gets the wrapped object or null.
      *
-     * @param pooledObject the PooledObject to test.
-     * @return whether the given PooledObject is null <em>or</em> contains a 
null.
+     * @param <T> the type of object in the pool.
+     * @param pooledObject the PooledObject to unwrap, may be null.
+     * @return the wrapped object or null.
+     * @since 2.13.0
+     */
+    static <T> T getObject(final PooledObject<T> pooledObject) {
+        return pooledObject != null ? pooledObject.getObject() : null;
+    }
+
+    /**
+     * Tests whether the given PooledObject is null <em>or</em> wraps a null.
+     *
+     * @param pooledObject the PooledObject to test, may be null.
+     * @return whether the given PooledObject is null <em>or</em> wraps a null.
      * @since 2.12.0
      */
     static boolean isNull(final PooledObject<?> pooledObject) {
-        return pooledObject == null || pooledObject.getObject() == null;
+        return getObject(pooledObject) == null;
+    }
+
+    /**
+     * Tests whether the given PooledObject isn't null <em>and</em> doesn't 
wraps a null.
+     *
+     * @param pooledObject the PooledObject to test, may be null.
+     * @return whether the given PooledObject isn't null <em>and</em> doesn't 
wraps a null.
+     * @since 2.13.0
+     */
+    static boolean nonNull(final PooledObject<?> pooledObject) {
+        return getObject(pooledObject) != null;
     }
 
     /**
diff --git a/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java 
b/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java
index 5c5a662039..03463455c7 100644
--- a/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java
+++ b/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java 
b/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java
index b405f94711..9ff127581b 100644
--- a/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java
+++ b/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java 
b/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java
index c80b05b6a5..7e45d84887 100644
--- a/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java
+++ b/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java 
b/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java
index cac0ce2da6..4c22ddff5d 100644
--- a/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java
+++ b/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java 
b/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java
index 6b86754d98..4de577d064 100644
--- a/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java
+++ b/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java
index 92797c8b45..38178c7d00 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java
index 2e1599fd52..1c6ee31868 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -205,41 +205,53 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
      * @param <T> type of objects in the pool
      */
     static class IdentityWrapper<T> {
+
+        /**
+         * Creates a new instance for the object inside a {@link PooledObject}.
+         *
+         * @param <T> The type of the object in the {@link PooledObject}.
+         * @param p The {@link PooledObject}.
+         * @return a new instance.
+         */
+        static <T> IdentityWrapper<T> unwrap(final PooledObject<T> p) {
+            return new IdentityWrapper<>(p.getObject());
+        }
+
         /** Wrapped object */
-        private final T instance;
+        private final T object;
 
         /**
          * Constructs a wrapper for an instance.
          *
-         * @param instance object to wrap
+         * @param object object to wrap
          */
-        IdentityWrapper(final T instance) {
-            this.instance = instance;
+        IdentityWrapper(final T object) {
+            this.object = object;
         }
 
         @Override
         @SuppressWarnings("rawtypes")
         public boolean equals(final Object other) {
-            return other instanceof IdentityWrapper && ((IdentityWrapper) 
other).instance == instance;
+            return other instanceof IdentityWrapper && ((IdentityWrapper) 
other).object == object;
         }
 
         /**
          * @return the wrapped object
          */
-        public T getObject() {
-            return instance;
+        T getObject() {
+            return object;
         }
 
         @Override
         public int hashCode() {
-            return System.identityHashCode(instance);
+            return System.identityHashCode(object);
         }
 
         @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder();
             builder.append("IdentityWrapper [instance=");
-            builder.append(instance);
+            builder.append(object);
             builder.append("]");
             return builder.toString();
         }
@@ -395,6 +407,7 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
 
     private volatile SwallowedExceptionListener swallowedExceptionListener;
     private volatile boolean messageStatistics;
+    private volatile boolean collectDetailedStatistics = 
BaseObjectPoolConfig.DEFAULT_COLLECT_DETAILED_STATISTICS;
 
     /** Additional configuration properties for abandoned object tracking. */
     protected volatile AbandonedConfig abandonedConfig;
@@ -482,6 +495,16 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
         return remove;
     }
 
+    /**
+     * Returns the duration since the given start time.
+     *
+     * @param startInstant the start time
+     * @return the duration since the given start time
+     */
+    final Duration durationSince(final Instant startInstant) {
+        return Duration.between(startInstant, Instant.now());
+    }
+
     /**
      * Tries to ensure that the configured minimum number of idle instances are
      * available in the pool.
@@ -524,6 +547,20 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
         return borrowedCount.get();
     }
 
+    /**
+     * Gets whether detailed timing statistics collection is enabled.
+     * When {@code false}, the pool will not collect detailed timing 
statistics for
+     * mean active time, mean idle time, and mean borrow wait time,
+     * improving performance under high load.
+     *
+     * @return {@code true} if detailed statistics collection is enabled,
+     *         {@code false} if disabled for improved performance.
+     * @since 2.13.0
+     */
+    public boolean getCollectDetailedStatistics() {
+        return collectDetailedStatistics;
+    }
+
     /**
      * Gets the total number of objects created for this pool over the 
lifetime of
      * the pool.
@@ -1226,8 +1263,9 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
     }
 
     /**
-     * Tests whether this pool instance been closed.
-     * @return {@code true} when this pool has been closed.
+     * Tests whether this pool instance is closed.
+     *
+     * @return {@code true} when this pool is closed.
      */
     public final boolean isClosed() {
         return closed;
@@ -1258,7 +1296,7 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
         }
         while (!registered) {
             try {
-                ObjectName objName;
+                final ObjectName objName;
                 // Skip the numeric suffix for the first pool in case there is
                 // only one so the names are cleaner.
                 if (i == 1) {
@@ -1348,6 +1386,21 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
         this.blockWhenExhausted = blockWhenExhausted;
     }
 
+    /**
+     * Sets whether detailed timing statistics collection is enabled.
+     * When {@code false}, the pool will not collect detailed timing 
statistics,
+     * improving performance under high load at the cost of reduced monitoring 
capabilities.
+     * <p>
+     * This setting affects data collection for mean active time, mean idle 
time, and mean borrow wait time.
+     * </p>
+     *
+     * @param collectDetailedStatistics whether to collect detailed statistics.
+     * @since 2.13.0
+     */
+    public void setCollectDetailedStatistics(final boolean 
collectDetailedStatistics) {
+        this.collectDetailedStatistics = collectDetailedStatistics;
+    }
+
     /**
      * Sets the receiver with the given configuration.
      *
@@ -1374,6 +1427,7 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
             setEvictionPolicy(policy);
         }
         setEvictorShutdownTimeout(config.getEvictorShutdownTimeoutDuration());
+        setCollectDetailedStatistics(config.getCollectDetailedStatistics());
     }
 
     /**
@@ -1945,6 +1999,7 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
         startEvictor(Duration.ofMillis(-1L));
     }
 
+
     /**
      * Swallows an exception and notifies the configured listener for swallowed
      * exceptions queue.
@@ -2045,17 +2100,19 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
      */
     final void updateStatsBorrow(final PooledObject<T> p, final Duration 
waitDuration) {
         borrowedCount.incrementAndGet();
-        idleTimes.add(p.getIdleDuration());
-        waitTimes.add(waitDuration);
-
-        // lock-free optimistic-locking maximum
-        Duration currentMaxDuration;
-        do {
-            currentMaxDuration = maxBorrowWaitDuration.get();
-            if (currentMaxDuration.compareTo(waitDuration) >= 0) {
-                break;
-            }
-        } while (!maxBorrowWaitDuration.compareAndSet(currentMaxDuration, 
waitDuration));
+        // Only collect detailed statistics if enabled
+        if (collectDetailedStatistics) {
+            idleTimes.add(p.getIdleDuration());
+            waitTimes.add(waitDuration);
+            // lock-free optimistic-locking maximum
+            Duration currentMaxDuration;
+            do {
+                currentMaxDuration = maxBorrowWaitDuration.get();
+                if (currentMaxDuration.compareTo(waitDuration) >= 0) {
+                    break;
+                }
+            } while (!maxBorrowWaitDuration.compareAndSet(currentMaxDuration, 
waitDuration));
+        }
     }
 
     /**
@@ -2066,7 +2123,25 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject implements Aut
      */
     final void updateStatsReturn(final Duration activeTime) {
         returnedCount.incrementAndGet();
-        activeTimes.add(activeTime);
+        // Only collect detailed statistics if enabled
+        if (collectDetailedStatistics) {
+            activeTimes.add(activeTime);
+        }
+    }
+
+    /**
+     * Waits for notification on the given object for the specified duration.
+     * Duration.ZERO causes the thread to wait indefinitely.
+     *
+     * @param obj the object to wait on
+     * @param duration the duration to wait
+     * @throws InterruptedException if interrupted while waiting
+     * @throws IllegalArgumentException if the duration is negative
+     */
+    final void wait(final Object obj, final Duration duration) throws 
InterruptedException {
+        if (!duration.isNegative()) {
+            obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
+        }
     }
 
 }
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java
index 38d7aa523e..8c278022c7 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -254,6 +254,19 @@ public abstract class BaseObjectPoolConfig<T> extends 
BaseObject implements Clon
      */
     public static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = 
DefaultEvictionPolicy.class.getName();
 
+    /**
+     * The default value for the {@code collectDetailedStatistics} 
configuration
+     * attribute. When {@code true}, the pool will collect detailed timing 
statistics
+     * for monitoring purposes. When {@code false}, detailed statistics 
collection
+     * is disabled, improving performance under high load.
+     * <p>
+     * This setting affects data collection for mean active time, mean idle 
time, and mean borrow wait time.
+     * </p>
+     *
+     * @since 2.13.0
+     */
+    public static final boolean DEFAULT_COLLECT_DETAILED_STATISTICS = true;
+
     private boolean lifo = DEFAULT_LIFO;
 
     private boolean fairness = DEFAULT_FAIRNESS;
@@ -291,6 +304,15 @@ public abstract class BaseObjectPoolConfig<T> extends 
BaseObject implements Clon
 
     private String jmxNameBase = DEFAULT_JMX_NAME_BASE;
 
+    private boolean collectDetailedStatistics = 
DEFAULT_COLLECT_DETAILED_STATISTICS;
+
+    /**
+     * Constructs a new instance.
+     */
+    public BaseObjectPoolConfig() {
+        // empty
+    }
+
     /**
      * Gets the value for the {@code blockWhenExhausted} configuration 
attribute for pools created with this configuration instance.
      *
@@ -302,6 +324,23 @@ public abstract class BaseObjectPoolConfig<T> extends 
BaseObject implements Clon
         return blockWhenExhausted;
     }
 
+    /**
+     * Gets the value for the {@code collectDetailedStatistics} configuration 
attribute
+     * for pools created with this configuration instance.
+     * <p>
+     * This setting affects data collection for mean active time, mean idle 
time, and mean borrow wait time.
+     * </p>
+     *
+     * @return  {@code true} if detailed statistics collection is enabled,
+     *          {@code false} if disabled for improved performance.
+     * @see GenericObjectPool#getCollectDetailedStatistics()
+     * @see GenericKeyedObjectPool#getCollectDetailedStatistics()
+     * @since 2.13.0
+     */
+    public boolean getCollectDetailedStatistics() {
+        return collectDetailedStatistics;
+    }
+
     /**
      * Gets the value for the {@code timeBetweenEvictionRuns} configuration 
attribute for pools created with this configuration instance.
      *
@@ -623,6 +662,25 @@ public abstract class BaseObjectPoolConfig<T> extends 
BaseObject implements Clon
         this.blockWhenExhausted = blockWhenExhausted;
     }
 
+    /**
+     * Sets the value for the {@code collectDetailedStatistics} configuration 
attribute
+     * for pools created with this configuration instance. When {@code false}, 
the pool
+     * will not collect detailed timing statistics, improving performance 
under high load
+     * at the cost of reduced monitoring capabilities.
+     * <p>
+     * This setting affects data collection for mean active time, mean idle 
time, and mean borrow wait time.
+     * </p>
+     *
+     * @param collectDetailedStatistics The new setting of {@code 
collectDetailedStatistics}
+     *        for this configuration instance.
+     * @see GenericObjectPool#getCollectDetailedStatistics()
+     * @see GenericKeyedObjectPool#getCollectDetailedStatistics()
+     * @since 2.13.0
+     */
+    public void setCollectDetailedStatistics(final boolean 
collectDetailedStatistics) {
+        this.collectDetailedStatistics = collectDetailedStatistics;
+    }
+
     /**
      * Sets the value for the {@code evictionPolicyClass} configuration 
attribute for pools created with this configuration instance.
      *
@@ -953,5 +1011,7 @@ public abstract class BaseObjectPoolConfig<T> extends 
BaseObject implements Clon
         builder.append(jmxNamePrefix);
         builder.append(", jmxNameBase=");
         builder.append(jmxNameBase);
+        builder.append(", collectDetailedStatistics=");
+        builder.append(collectDetailedStatistics);
     }
 }
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java
index 95b98fde91..c3fe834b41 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java
index ffe607242e..e04145c90e 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -42,6 +42,13 @@ import org.apache.tomcat.dbcp.pool2.PooledObject;
  */
 public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {
 
+    /**
+     * Constructs a new instance.
+     */
+    public DefaultEvictionPolicy() {
+        // empty
+    }
+
     @Override
     public boolean evict(final EvictionConfig config, final PooledObject<T> 
underTest, final int idleCount) {
         // @formatter:off
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java
index 5c0c3af543..f04b89292f 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java
index 30854e2fad..40efe6c109 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java
index 3e8ad6ea04..7d6d49c3b5 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java
index b6345187bd..5e23a2bca4 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java
index 281ca367bd..28ee6c595e 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -23,7 +23,7 @@ import org.apache.tomcat.dbcp.pool2.PooledObject;
  * DefaultEvictionPolicy} for a pool, users must provide an implementation of
  * this interface that provides the required eviction policy.
  *
- * @param <T> the type of objects in the pool
+ * @param <T> the type of objects in the pool.
  * @since 2.0
  */
 public interface EvictionPolicy<T> {
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java
index 58fa30954d..2889278318 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -21,6 +21,7 @@ import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.time.Duration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -73,11 +74,17 @@ final class EvictionTimer {
         @Override
         public void run() {
             synchronized (EvictionTimer.class) {
-                for (final 
Entry<WeakReference<BaseGenericObjectPool<?>.Evictor>, 
WeakRunner<BaseGenericObjectPool<?>.Evictor>> entry : TASK_MAP
-                        .entrySet()) {
+                /*
+                 * Need to use iterator over TASK_MAP so entries can be 
removed when iterating without triggering a
+                 * ConcurrentModificationException.
+                 */
+                final 
Iterator<Entry<WeakReference<BaseGenericObjectPool<?>.Evictor>, 
WeakRunner<BaseGenericObjectPool<?>.Evictor>>> iterator =
+                        TASK_MAP.entrySet().iterator();
+                while (iterator.hasNext()) {
+                    final 
Entry<WeakReference<BaseGenericObjectPool<?>.Evictor>, 
WeakRunner<BaseGenericObjectPool<?>.Evictor>> entry = iterator.next();
                     if (entry.getKey().get() == null) {
                         executor.remove(entry.getValue());
-                        TASK_MAP.remove(entry.getKey());
+                        iterator.remove();
                     }
                 }
                 if (TASK_MAP.isEmpty() && executor != null) {
@@ -113,8 +120,10 @@ final class EvictionTimer {
             if (task != null) {
                 task.run();
             } else {
-                executor.remove(this);
-                TASK_MAP.remove(ref);
+                synchronized (EvictionTimer.class) {
+                    executor.remove(this);
+                    TASK_MAP.remove(ref);
+                }
             }
         }
     }
@@ -195,8 +204,8 @@ final class EvictionTimer {
      * server environments.
      *
      * @param task      Task to be scheduled.
-     * @param delay     Delay in milliseconds before task is executed.
-     * @param period    Time in milliseconds between executions.
+     * @param delay     Duration before task is executed.
+     * @param period    Duration between executions.
      */
     static synchronized void schedule(
             final BaseGenericObjectPool<?>.Evictor task, final Duration delay, 
final Duration period) {
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java
index 07e89b6690..763ba12e71 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -28,8 +28,8 @@ import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.TreeMap;
+import java.util.concurrent.BlockingDeque;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
@@ -128,16 +128,16 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
          * @param fairness true means client threads waiting to borrow / 
return instances
          * will be served as if waiting in a FIFO queue.
          */
-        ObjectDeque(final boolean fairness) {
+        private ObjectDeque(final boolean fairness) {
             idleObjects = new LinkedBlockingDeque<>(fairness);
         }
 
         /**
          * Gets all the objects for the current key.
          *
-         * @return All the objects
+         * @return All the objects,
          */
-        public Map<IdentityWrapper<S>, PooledObject<S>> getAllObjects() {
+        Map<IdentityWrapper<S>, PooledObject<S>> getAllObjects() {
             return allObjects;
         }
 
@@ -145,27 +145,27 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
          * Gets the number of instances created - number destroyed.
          * Should always be less than or equal to maxTotalPerKey.
          *
-         * @return The net instance addition count for this deque
+         * @return The net instance addition count for this deque.
          */
-        public AtomicInteger getCreateCount() {
+        AtomicInteger getCreateCount() {
             return createCount;
         }
 
         /**
          * Gets the idle objects for the current key.
          *
-         * @return The idle objects
+         * @return The idle objects.
          */
-        public LinkedBlockingDeque<PooledObject<S>> getIdleObjects() {
+        LinkedBlockingDeque<PooledObject<S>> getIdleObjects() {
             return idleObjects;
         }
 
         /**
          * Gets the number of threads with an interest registered in this key.
          *
-         * @return The number of threads with a registered interest in this key
+         * @return The number of threads with a registered interest in this 
key.
          */
-        public AtomicLong getNumInterested() {
+        AtomicLong getNumInterested() {
             return numInterested;
         }
 
@@ -201,6 +201,12 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
     private volatile int maxTotalPerKey =
             GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
 
+    private volatile boolean reuseCapacityOnReturn =
+            GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_RETURN;
+
+    private volatile boolean reuseCapacityOnMaintenance =
+            GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE;
+
     private final KeyedPooledObjectFactory<K, T> factory;
 
     private final boolean fairness;
@@ -298,9 +304,9 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
      * @throws Exception If the associated factory fails to passivate the 
object
      */
     private void addIdleObject(final K key, final PooledObject<T> p) throws 
Exception {
-        if (!PooledObject.isNull(p)) {
+        if (PooledObject.nonNull(p)) {
             factory.passivateObject(key, p);
-            final LinkedBlockingDeque<PooledObject<T>> idleObjects = 
poolMap.get(key).getIdleObjects();
+            final BlockingDeque<PooledObject<T>> idleObjects = 
poolMap.get(key).getIdleObjects();
             if (getLifo()) {
                 idleObjects.addFirst(p);
             } else {
@@ -330,9 +336,18 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
     @Override
     public void addObject(final K key) throws Exception {
         assertOpen();
-        register(key);
+        final ObjectDeque<T> objectDeque = register(key);
         try {
-            addIdleObject(key, create(key));
+            // Attempt create and add only if there is capacity to add
+            // > to the overall instance count
+            // > to the pool under the key
+            final int maxtTotalPerKey = getMaxTotalPerKey();
+            final int maxTotal = getMaxTotal();
+            if ((maxTotal < 0 || getNumActive() + getNumIdle() < maxTotal)
+                    && (maxtTotalPerKey < 0 || objectDeque.allObjects.size() < 
maxtTotalPerKey)) {
+                // Attempt to create and add a new instance under key
+                addIdleObject(key, create(key, getMaxWaitDuration()));
+            }
         } finally {
             deregister(key);
         }
@@ -400,8 +415,8 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
      * </p>
      *
      * @param key pool key
-     * @param borrowMaxWaitMillis The time to wait in milliseconds for an 
object
-     *                            to become available
+     * @param maxWaitDuration The time to wait for an object to become
+     *                        available
      *
      * @return object instance from the keyed pool
      * @throws NoSuchElementException if a keyed object instance cannot be
@@ -409,10 +424,12 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
      *
      * @throws Exception if a keyed object instance cannot be returned due to 
an
      *                   error
+     * @since 2.12.2
      */
-    public T borrowObject(final K key, final long borrowMaxWaitMillis) throws 
Exception {
+    public T borrowObject(final K key, final Duration maxWaitDuration) throws 
Exception {
         assertOpen();
-
+        final Instant startInstant = Instant.now();
+        Duration remainingWaitDuration = maxWaitDuration;
         final AbandonedConfig ac = this.abandonedConfig;
         if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 
&&
                 getNumActive() > getMaxTotal() - 3) {
@@ -426,27 +443,28 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         final boolean blockWhenExhausted = getBlockWhenExhausted();
 
         boolean create;
-        final Instant waitTime = Instant.now();
         final ObjectDeque<T> objectDeque = register(key);
 
         try {
             while (p == null) {
+                remainingWaitDuration = 
maxWaitDuration.minus(durationSince(startInstant));
                 create = false;
                 p = objectDeque.getIdleObjects().pollFirst();
                 if (p == null) {
-                    p = create(key);
-                    if (!PooledObject.isNull(p)) {
+                    p = create(key, remainingWaitDuration);
+                    if (PooledObject.nonNull(p)) {
                         create = true;
                     }
+                    remainingWaitDuration = 
maxWaitDuration.minus(durationSince(startInstant));
                 }
                 if (blockWhenExhausted) {
                     if (PooledObject.isNull(p)) {
-                        p = borrowMaxWaitMillis < 0 ? 
objectDeque.getIdleObjects().takeFirst():
-                                
objectDeque.getIdleObjects().pollFirst(borrowMaxWaitMillis, 
TimeUnit.MILLISECONDS);
+                        p = maxWaitDuration.isNegative() ? 
objectDeque.getIdleObjects().takeFirst()
+                                : 
objectDeque.getIdleObjects().pollFirst(remainingWaitDuration);
                     }
                     if (PooledObject.isNull(p)) {
                         throw new NoSuchElementException(appendStats(
-                                "Timeout waiting for idle object, 
borrowMaxWaitMillis=" + borrowMaxWaitMillis));
+                                "Timeout waiting for idle object, 
borrowMaxWaitMillis=" + maxWaitDuration.toMillis()));
                     }
                 } else if (PooledObject.isNull(p)) {
                     throw new NoSuchElementException(appendStats("Pool 
exhausted"));
@@ -455,7 +473,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                     p = null;
                 }
 
-                if (!PooledObject.isNull(p)) {
+                if (PooledObject.nonNull(p)) {
                     try {
                         factory.activateObject(key, p);
                     } catch (final Exception e) {
@@ -466,12 +484,13 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                         }
                         p = null;
                         if (create) {
-                            final NoSuchElementException nsee = new 
NoSuchElementException(appendStats("Unable to activate object"));
+                            final NoSuchElementException nsee = new 
NoSuchElementException(
+                                    appendStats("Unable to activate object"));
                             nsee.initCause(e);
                             throw nsee;
                         }
                     }
-                    if (!PooledObject.isNull(p) && getTestOnBorrow()) {
+                    if (PooledObject.nonNull(p) && getTestOnBorrow()) {
                         boolean validate = false;
                         Throwable validationThrowable = null;
                         try {
@@ -502,11 +521,78 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             deregister(key);
         }
 
-        updateStatsBorrow(p, Duration.between(waitTime, Instant.now()));
+        updateStatsBorrow(p, Duration.between(startInstant, Instant.now()));
 
         return p.getObject();
     }
 
+
+    /**
+     * Borrows an object from the sub-pool associated with the given key using
+     * the specified waiting time which only applies if
+     * {@link #getBlockWhenExhausted()} is true.
+     * <p>
+     * If there is one or more idle instances available in the sub-pool
+     * associated with the given key, then an idle instance will be selected
+     * based on the value of {@link #getLifo()}, activated and returned.  If
+     * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to
+     * {@code 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 sub-pool associated with
+     * the given key, behavior depends on the {@link #getMaxTotalPerKey()
+     * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable)
+     * {@link #getBlockWhenExhausted()} and the value passed in to the
+     * {@code borrowMaxWaitMillis} parameter. If the number of instances 
checked
+     * out from the sub-pool under the given key is less than
+     * {@code maxTotalPerKey} and the total number of instances in
+     * circulation (under all keys) is less than {@code maxTotal}, a new
+     * instance is created, activated and (if applicable) validated and 
returned
+     * to the caller. If validation fails, a {@code NoSuchElementException}
+     * will be thrown. If the factory returns null when creating an instance,
+     * a {@code NullPointerException} is thrown.
+     * </p>
+     * <p>
+     * If the associated sub-pool is exhausted (no available idle instances and
+     * no capacity to create new ones), this method will either block
+     * ({@link #getBlockWhenExhausted()} is true) or throw a
+     * {@code NoSuchElementException}
+     * ({@link #getBlockWhenExhausted()} is false).
+     * The length of time that this method will block when
+     * {@link #getBlockWhenExhausted()} is true is determined by the value
+     * passed in to the {@code borrowMaxWait} parameter.
+     * </p>
+     * <p>
+     * When {@code maxTotal} is set to a positive value and this method is
+     * invoked when at the limit with no idle instances available under the 
requested
+     * key, an attempt is made to create room by clearing the oldest 15% of the
+     * elements from the keyed sub-pools.
+     * </p>
+     * <p>
+     * When the pool is exhausted, multiple calling threads may be
+     * simultaneously blocked waiting for instances to become available. A
+     * "fairness" algorithm has been implemented to ensure that threads receive
+     * available instances in request arrival order.
+     * </p>
+     *
+     * @param key pool key
+     * @param maxWaitMillis The time to wait in milliseconds for an object to 
become
+     *                        available
+     *
+     * @return object instance from the keyed pool
+     * @throws NoSuchElementException if a keyed object instance cannot be
+     *                                returned because the pool is exhausted.
+     *
+     * @throws Exception if a keyed object instance cannot be returned due to 
an
+     *                   error
+     */
+
+    public T borrowObject(final K key, final long maxWaitMillis) throws 
Exception {
+        return borrowObject(key, Duration.ofMillis(maxWaitMillis));
+    }
+
     /**
      * Calculate the number of objects that need to be created to attempt to
      * maintain the minimum number of idle objects while not exceeded the 
limits
@@ -607,7 +693,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         final ObjectDeque<T> objectDeque = register(key);
         int freedCapacity = 0;
         try {
-            final LinkedBlockingDeque<PooledObject<T>> idleObjects = 
objectDeque.getIdleObjects();
+            final BlockingDeque<PooledObject<T>> idleObjects = 
objectDeque.getIdleObjects();
             PooledObject<T> p = idleObjects.poll();
             while (p != null) {
                 try {
@@ -708,11 +794,16 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
     /**
      * Creates a new pooled object or null.
      *
-     * @param key Key associated with new pooled object.
+     * @param key             Key associated with new pooled object.
+     * @param maxWaitDuration The time to wait in this method. If negative or 
ZERO,
+     *                        this method may wait indefinitely.
      * @return The new, wrapped pooled object. May return null.
      * @throws Exception If the objection creation fails.
      */
-    private PooledObject<T> create(final K key) throws Exception {
+    private PooledObject<T> create(final K key, final Duration 
maxWaitDuration) throws Exception {
+        final Instant startInstant = Instant.now();
+        Duration remainingWaitDuration = maxWaitDuration.isNegative() ? 
Duration.ZERO : maxWaitDuration;
+
         int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key
         if (maxTotalPerKeySave < 0) {
             maxTotalPerKeySave = Integer.MAX_VALUE;
@@ -744,6 +835,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         //          call the factory
         Boolean create = null;
         while (create == null) {
+            remainingWaitDuration = maxWaitDuration.isNegative() ? 
Duration.ZERO : maxWaitDuration.minus(durationSince(startInstant));
             synchronized (objectDeque.makeObjectCountLock) {
                 final long newCreateCount = 
objectDeque.getCreateCount().incrementAndGet();
                 // Check against the per key limit
@@ -757,12 +849,13 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                         // create a new object. Return and wait for an object 
to
                         // be returned.
                         create = Boolean.FALSE;
-                    } else {
+                    } else if (!remainingWaitDuration.isNegative()) {
                         // There are makeObject() calls in progress that might
                         // bring the pool to capacity. Those calls might also
                         // fail so wait until they complete and then re-test if
                         // the pool is at capacity or not.
-                        objectDeque.makeObjectCountLock.wait();
+                        
objectDeque.makeObjectCountLock.wait(remainingWaitDuration.toMillis(),
+                                 remainingWaitDuration.getNano() % 1_000_000);
                     }
                 } else {
                     // The pool is not at capacity. Create a new object.
@@ -808,7 +901,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         }
 
         createdCount.incrementAndGet();
-        objectDeque.getAllObjects().put(new IdentityWrapper<>(p.getObject()), 
p);
+        objectDeque.getAllObjects().put(IdentityWrapper.unwrap(p), p);
         return p;
     }
 
@@ -857,13 +950,13 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
     /**
      * Destroy the wrapped, pooled object.
      *
-     * @param key The key associated with the object to destroy.
+     * @param key The key associated with the object to destroy
      * @param toDestroy The wrapped object to be destroyed
      * @param always Should the object be destroyed even if it is not currently
      *               in the set of idle objects for the given key
-     * @param destroyMode DestroyMode context provided to the factory
+     * @param destroyMode {@link DestroyMode} context provided to the factory
      * @return {@code true} if the object was destroyed, otherwise {@code 
false}
-     * @throws Exception If the object destruction failed
+     * @throws Exception If the factory throws an exception during destruction
      */
     private boolean destroy(final K key, final PooledObject<T> toDestroy, 
final boolean always, final DestroyMode destroyMode) throws Exception {
 
@@ -881,7 +974,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                 }
             }
             if (isIdle || always) {
-                objectDeque.getAllObjects().remove(new 
IdentityWrapper<>(toDestroy.getObject()));
+                
objectDeque.getAllObjects().remove(IdentityWrapper.unwrap(toDestroy));
                 toDestroy.invalidate();
 
                 try {
@@ -1086,8 +1179,6 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                             }
                         }
                         underTest.endEvictionTest(idleObjects);
-                        // TODO - May need to add code here once additional
-                        // states are used
                     }
                 }
             }
@@ -1096,6 +1187,9 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
             removeAbandoned(ac);
         }
+        if (reuseCapacityOnMaintenance) {
+            reuseCapacity();
+        }
     }
 
     /**
@@ -1258,6 +1352,32 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         return result;
     }
 
+    /**
+     * Gets whether to call {@link #reuseCapacity()} during pool maintenance 
(eviction).
+     * When true, the pool will attempt to reuse freed capacity at the end of 
each
+     * eviction run.
+     *
+     * @return {@code true} if capacity reuse is enabled during maintenance, 
{@code false} otherwise
+     * @see #setReuseCapacityOnMaintenance(boolean)
+     * @since 2.13.0
+     */
+    public boolean getReuseCapacityOnMaintenance() {
+        return reuseCapacityOnMaintenance;
+    }
+
+    /**
+     * Gets whether to call {@link #reuseCapacity()} when returning objects to 
the pool.
+     * When true, the pool will check if there are threads waiting to borrow 
objects
+     * and attempt to reuse the capacity freed by the return operation.
+     *
+     * @return {@code true} if capacity reuse is enabled on return, {@code 
false} otherwise
+     * @see #setReuseCapacityOnReturn(boolean)
+     * @since 2.13.0
+     */
+    public boolean getReuseCapacityOnReturn() {
+        return reuseCapacityOnReturn;
+    }
+
     @SuppressWarnings("boxing") // Commons Pool uses auto-boxing
     @Override
     String getStatsString() {
@@ -1333,7 +1453,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
      * to be borrowed) and active (currently borrowed).
      * <p>
      * Note: This is named listAllObjects so it is presented as an operation 
via
-     * JMX. That means it won't be invoked unless the explicitly requested
+     * JMX. That means it won't be invoked unless explicitly requested
      * whereas all attributes will be automatically requested when viewing the
      * attributes for an object in a tool like JConsole.
      * </p>
@@ -1505,7 +1625,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             }
 
             final int maxIdle = getMaxIdlePerKey();
-            final LinkedBlockingDeque<PooledObject<T>> idleObjects = 
objectDeque.getIdleObjects();
+            final BlockingDeque<PooledObject<T>> idleObjects = 
objectDeque.getIdleObjects();
 
             if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) {
                 try {
@@ -1527,7 +1647,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                 }
             }
         } finally {
-            if (hasBorrowWaiters()) {
+            if (reuseCapacityOnReturn && hasBorrowWaiters()) {
                 reuseCapacity();
             }
             updateStatsReturn(activeTime);
@@ -1550,7 +1670,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
     private void reuseCapacity() {
         final int maxTotalPerKeySave = getMaxTotalPerKey();
         int maxQueueLength = 0;
-        LinkedBlockingDeque<PooledObject<T>> mostLoadedPool = null;
+        BlockingDeque<PooledObject<T>> mostLoadedPool = null;
         K mostLoadedKey = null;
 
         // Find the most loaded pool that could take a new instance
@@ -1571,7 +1691,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             try {
                 // If there is no capacity to add, create will return null
                 // and addIdleObject will no-op.
-                addIdleObject(mostLoadedKey, create(mostLoadedKey));
+                addIdleObject(mostLoadedKey, create(mostLoadedKey, 
Duration.ZERO));
             } catch (final Exception e) {
                 swallowException(e);
             } finally {
@@ -1585,7 +1705,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
      * <p>
      * Always activates {@link #reuseCapacity()} at least once.
      *
-     * @param newCapacity number of new instances to attempt to create.
+     * @param newCapacity number of times to call {@link #reuseCapacity()}
      */
     private void reuseCapacity(final int newCapacity) {
         final int bound = newCapacity < 1 ? 1 : newCapacity;
@@ -1606,6 +1726,8 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         setMaxTotalPerKey(conf.getMaxTotalPerKey());
         setMaxTotal(conf.getMaxTotal());
         setMinIdlePerKey(conf.getMinIdlePerKey());
+        setReuseCapacityOnReturn(conf.getReuseCapacityOnReturn());
+        setReuseCapacityOnMaintenance(conf.getReuseCapacityOnMaintenance());
     }
 
     /**
@@ -1662,6 +1784,34 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         this.minIdlePerKey = minIdlePerKey;
     }
 
+    /**
+     * Sets whether to call {@link #reuseCapacity()} during pool maintenance 
(eviction).
+     * When enabled, the pool will attempt to reuse capacity at the end of each
+     * eviction run.
+     *
+     * @param reuseCapacityOnMaintenance {@code true} to enable capacity reuse 
during
+     *                                   maintenance, {@code false} to disable
+     * @see #getReuseCapacityOnMaintenance()
+     * @since 2.13.0
+     */
+    public void setReuseCapacityOnMaintenance(final boolean 
reuseCapacityOnMaintenance) {
+        this.reuseCapacityOnMaintenance = reuseCapacityOnMaintenance;
+    }
+
+    /**
+     * Sets whether to call {@link #reuseCapacity()} when returning objects to 
the pool.
+     * When enabled, the pool will check if there are threads waiting to 
borrow objects
+     * and attempt to reuse capacity (across pools) on return.
+     *
+     * @param reuseCapacityOnReturn {@code true} to enable capacity reuse on 
return,
+     *                              {@code false} to disable
+     * @see #getReuseCapacityOnReturn()
+     * @since 2.13.0
+     */
+    public void setReuseCapacityOnReturn(final boolean reuseCapacityOnReturn) {
+        this.reuseCapacityOnReturn = reuseCapacityOnReturn;
+    }
+
     @Override
     protected void toStringAppendFields(final StringBuilder builder) {
         super.toStringAppendFields(builder);
@@ -1689,6 +1839,10 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         builder.append(evictionKey);
         builder.append(", abandonedConfig=");
         builder.append(abandonedConfig);
+        builder.append(", reuseCapacityOnReturn=");
+        builder.append(reuseCapacityOnReturn);
+        builder.append(", reuseCapacityOnMaintenance=");
+        builder.append(reuseCapacityOnMaintenance);
     }
 
     /**
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java
index 14fe7b139c..97c45449e0 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -58,6 +58,22 @@ public class GenericKeyedObjectPoolConfig<T> extends 
BaseObjectPoolConfig<T> {
      */
     public static final int DEFAULT_MAX_IDLE_PER_KEY = 8;
 
+    /**
+     * The default value for the {@code reuseCapacityOnReturn} configuration 
attribute: {@value}.
+     *
+     * @see GenericKeyedObjectPool#getReuseCapacityOnReturn()
+     * @since 2.13.0
+     */
+    public static final boolean DEFAULT_REUSE_CAPACITY_ON_RETURN = true;
+
+    /**
+     * The default value for the {@code reuseCapacityOnMaintenance} 
configuration attribute: {@value}.
+     *
+     * @see GenericKeyedObjectPool#getReuseCapacityOnMaintenance()
+     * @since 2.13.0
+     */
+    public static final boolean DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE = false;
+
     private int minIdlePerKey = DEFAULT_MIN_IDLE_PER_KEY;
 
     private int maxIdlePerKey = DEFAULT_MAX_IDLE_PER_KEY;
@@ -66,6 +82,10 @@ public class GenericKeyedObjectPoolConfig<T> extends 
BaseObjectPoolConfig<T> {
 
     private int maxTotal = DEFAULT_MAX_TOTAL;
 
+    private boolean reuseCapacityOnReturn = DEFAULT_REUSE_CAPACITY_ON_RETURN;
+
+    private boolean reuseCapacityOnMaintenance = 
DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE;
+
     /**
      * Constructs a new configuration with default settings.
      */
@@ -134,6 +154,34 @@ public class GenericKeyedObjectPoolConfig<T> extends 
BaseObjectPoolConfig<T> {
         return minIdlePerKey;
     }
 
+    /**
+     * Gets the value for the {@code reuseCapacityOnMaintenance} configuration 
attribute
+     * for pools created with this configuration instance.
+     *
+     * @return  The current setting of {@code reuseCapacityOnMaintenance} for 
this
+     *          configuration instance
+     *
+     * @see GenericKeyedObjectPool#getReuseCapacityOnMaintenance()
+     * @since 2.13.0
+     */
+    public boolean getReuseCapacityOnMaintenance() {
+        return reuseCapacityOnMaintenance;
+    }
+
+    /**
+     * Gets the value for the {@code reuseCapacityOnReturn} configuration 
attribute
+     * for pools created with this configuration instance.
+     *
+     * @return  The current setting of {@code reuseCapacityOnReturn} for this
+     *          configuration instance
+     *
+     * @see GenericKeyedObjectPool#getReuseCapacityOnReturn()
+     * @since 2.13.0
+     */
+    public boolean getReuseCapacityOnReturn() {
+        return reuseCapacityOnReturn;
+    }
+
     /**
      * Sets the value for the {@code maxIdlePerKey} configuration attribute for
      * pools created with this configuration instance.
@@ -186,6 +234,34 @@ public class GenericKeyedObjectPoolConfig<T> extends 
BaseObjectPoolConfig<T> {
         this.minIdlePerKey = minIdlePerKey;
     }
 
+    /**
+     * Sets the value for the {@code reuseCapacityOnMaintenance} configuration 
attribute for
+     * pools created with this configuration instance.
+     *
+     * @param reuseCapacityOnMaintenance The new setting of {@code 
reuseCapacityOnMaintenance}
+     *        for this configuration instance
+     *
+     * @see GenericKeyedObjectPool#setReuseCapacityOnMaintenance(boolean)
+     * @since 2.13.0
+     */
+    public void setReuseCapacityOnMaintenance(final boolean 
reuseCapacityOnMaintenance) {
+        this.reuseCapacityOnMaintenance = reuseCapacityOnMaintenance;
+    }
+
+    /**
+     * Sets the value for the {@code reuseCapacityOnReturn} configuration 
attribute for
+     * pools created with this configuration instance.
+     *
+     * @param reuseCapacityOnReturn The new setting of {@code 
reuseCapacityOnReturn}
+     *        for this configuration instance
+     *
+     * @see GenericKeyedObjectPool#setReuseCapacityOnReturn(boolean)
+     * @since 2.13.0
+     */
+    public void setReuseCapacityOnReturn(final boolean reuseCapacityOnReturn) {
+        this.reuseCapacityOnReturn = reuseCapacityOnReturn;
+    }
+
     @Override
     protected void toStringAppendFields(final StringBuilder builder) {
         super.toStringAppendFields(builder);
@@ -197,5 +273,9 @@ public class GenericKeyedObjectPoolConfig<T> extends 
BaseObjectPoolConfig<T> {
         builder.append(maxTotalPerKey);
         builder.append(", maxTotal=");
         builder.append(maxTotal);
+        builder.append(", reuseCapacityOnReturn=");
+        builder.append(reuseCapacityOnReturn);
+        builder.append(", reuseCapacityOnMaintenance=");
+        builder.append(reuseCapacityOnMaintenance);
     }
 }
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java
index 076e888662..6e267bd73c 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
index 98f9819cc0..20398ff9e2 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -84,12 +84,6 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
     private static final String ONAME_BASE =
         "org.apache.commons.pool2:type=GenericObjectPool,name=";
 
-    private static void wait(final Object obj, final Duration duration) throws 
InterruptedException {
-        if (!duration.isNegative()) {
-            obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
-        }
-    }
-
     private volatile String factoryType;
 
     private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
@@ -102,17 +96,17 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * All of the objects currently associated with this pool in any state. It
      * excludes objects that have been destroyed. The size of
      * {@link #allObjects} will always be less than or equal to {@link
-     * #_maxActive}. Map keys are pooled objects, values are the PooledObject
+     * #getMaxTotal()}. Map keys are pooled objects, values are the 
PooledObject
      * wrappers used internally by the pool.
      */
     private final ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>> 
allObjects = new ConcurrentHashMap<>();
 
     /*
      * The combined count of the currently created objects and those in the
-     * process of being created. Under load, it may exceed {@link #_maxActive}
+     * process of being created. Under load, it may exceed {@link 
#getMaxTotal()}
      * if multiple threads try and create a new object at the same time but
      * {@link #create()} will ensure that there are never more than
-     * {@link #_maxActive} objects created at any one time.
+     * {@link #getMaxTotal()} objects created at any one time.
      */
     private final AtomicLong createCount = new AtomicLong();
 
@@ -188,7 +182,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * @throws Exception If the factory fails to passivate the object
      */
     private void addIdleObject(final PooledObject<T> p) throws Exception {
-        if (!PooledObject.isNull(p)) {
+        if (PooledObject.nonNull(p)) {
             factory.passivateObject(p);
             if (getLifo()) {
                 idleObjects.addFirst(p);
@@ -199,10 +193,11 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
     }
 
     /**
-     * Creates an object, and place it into the pool. addObject() is useful for
+     * Creates an object, and places it into the pool. addObject() is useful 
for
      * "pre-loading" a pool with idle objects.
      * <p>
-     * If there is no capacity available to add to the pool, this is a no-op
+     * If there is no capacity available to add to the pool, or there are 
already
+     * {@link #getMaxIdle()} idle instances in the pool, this is a no-op
      * (no exception, no impact to the pool).
      * </p>
      * <p>
@@ -217,11 +212,16 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         if (factory == null) {
             throw new IllegalStateException("Cannot add objects without a 
factory.");
         }
-        addIdleObject(create(getMaxWaitDuration()));
+
+        final int localMaxTotal = getMaxTotal();
+        final int localMaxIdle = getMaxIdle();
+        if (getNumIdle() < localMaxIdle && (localMaxTotal < 0 || 
createCount.get() < localMaxTotal)) {
+            addIdleObject(create(getMaxWaitDuration()));
+        }
     }
 
     /**
-     * Equivalent to <code>{@link #borrowObject(long)
+     * Equivalent to <code>{@link #borrowObject(Duration)
      * borrowObject}({@link #getMaxWaitDuration()})</code>.
      *
      * {@inheritDoc}
@@ -246,8 +246,8 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * <p>
      * If there are no idle instances available in the pool, behavior depends 
on
      * the {@link #getMaxTotal() maxTotal}, (if applicable)
-     * {@link #getBlockWhenExhausted()} and the value passed in to the
-     * {@code borrowMaxWaitMillis} parameter. If the number of instances
+     * {@link #getBlockWhenExhausted()} and the value passed in the
+     * {@code maxWaitDuration} parameter. If the number of instances
      * checked out from the pool is less than {@code maxTotal,} a new
      * instance is created, activated and (if applicable) validated and 
returned
      * to the caller. If validation fails, a {@code NoSuchElementException}
@@ -261,8 +261,9 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * {@code NoSuchElementException} (if
      * {@link #getBlockWhenExhausted()} is false). The length of time that this
      * method will block when {@link #getBlockWhenExhausted()} is true is
-     * determined by the value passed in to the {@code borrowMaxWaitMillis}
-     * parameter.
+     * determined by the value passed in to the {@code maxWaitDuration}
+     * parameter. Passing a negative duration will cause this method to block
+     * indefinitely until an object becomes available.
      * </p>
      * <p>
      * When the pool is exhausted, multiple calling threads may be
@@ -271,17 +272,17 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * available instances in request arrival order.
      * </p>
      *
-     * @param borrowMaxWaitDuration The time to wait for an object to become 
available, not null.
+     * @param maxWaitDuration The time to wait for an object to become 
available, not null.
      * @return object instance from the pool
      * @throws NoSuchElementException if an instance cannot be returned
      * @throws Exception if an object instance cannot be returned due to an 
error
      * @since 2.10.0
      */
-    public T borrowObject(final Duration borrowMaxWaitDuration) throws 
Exception {
+    public T borrowObject(final Duration maxWaitDuration) throws Exception {
         assertOpen();
         final Instant startInstant = Instant.now();
-        final boolean negativeDuration = borrowMaxWaitDuration.isNegative();
-        Duration remainingWaitDuration = borrowMaxWaitDuration;
+        final boolean negativeDuration = maxWaitDuration.isNegative();
+        Duration remainingWaitDuration = maxWaitDuration;
         final AbandonedConfig ac = this.abandonedConfig;
         if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 
&& getNumActive() > getMaxTotal() - 3) {
             removeAbandoned(ac);
@@ -292,22 +293,22 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         final boolean blockWhenExhausted = getBlockWhenExhausted();
         boolean create;
         while (p == null) {
-            remainingWaitDuration = 
remainingWaitDuration.minus(durationSince(startInstant));
+            remainingWaitDuration = 
maxWaitDuration.minus(durationSince(startInstant));
             create = false;
             p = idleObjects.pollFirst();
             if (p == null) {
                 p = create(remainingWaitDuration);
-                if (!PooledObject.isNull(p)) {
+                if (PooledObject.nonNull(p)) {
                     create = true;
                 }
             }
             if (blockWhenExhausted) {
                 if (PooledObject.isNull(p)) {
+                    remainingWaitDuration = 
maxWaitDuration.minus(durationSince(startInstant));
                     p = negativeDuration ? idleObjects.takeFirst() : 
idleObjects.pollFirst(remainingWaitDuration);
                 }
                 if (PooledObject.isNull(p)) {
-                    throw new NoSuchElementException(appendStats(
-                            "Timeout waiting for idle object, 
borrowMaxWaitDuration=" + remainingWaitDuration));
+                    throw new NoSuchElementException(appendStats("Timeout 
waiting for idle object, maxWaitDuration=" + remainingWaitDuration));
                 }
             } else if (PooledObject.isNull(p)) {
                 throw new NoSuchElementException(appendStats("Pool 
exhausted"));
@@ -326,8 +327,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                     }
                     p = null;
                     if (create) {
-                        final NoSuchElementException nsee = new 
NoSuchElementException(
-                                appendStats("Unable to activate object"));
+                        final NoSuchElementException nsee = new 
NoSuchElementException(appendStats("Unable to activate object"));
                         nsee.initCause(e);
                         throw nsee;
                     }
@@ -350,8 +350,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                         }
                         p = null;
                         if (create) {
-                            final NoSuchElementException nsee = new 
NoSuchElementException(
-                                    appendStats("Unable to validate object"));
+                            final NoSuchElementException nsee = new 
NoSuchElementException(appendStats("Unable to validate object"));
                             nsee.initCause(validationThrowable);
                             throw nsee;
                         }
@@ -363,10 +362,6 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         return p.getObject();
     }
 
-    private Duration durationSince(final Instant startInstant) {
-        return Duration.between(startInstant, Instant.now());
-    }
-
     /**
      * Borrows an object from the pool using the specific waiting time which 
only
      * applies if {@link #getBlockWhenExhausted()} is true.
@@ -383,7 +378,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * If there are no idle instances available in the pool, behavior depends 
on
      * the {@link #getMaxTotal() maxTotal}, (if applicable)
      * {@link #getBlockWhenExhausted()} and the value passed in to the
-     * {@code borrowMaxWaitMillis} parameter. If the number of instances
+     * {@code maxWaitMillis} parameter. If the number of instances
      * checked out from the pool is less than {@code maxTotal,} a new
      * instance is created, activated and (if applicable) validated and 
returned
      * to the caller. If validation fails, a {@code NoSuchElementException}
@@ -397,8 +392,9 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * {@code NoSuchElementException} (if
      * {@link #getBlockWhenExhausted()} is false). The length of time that this
      * method will block when {@link #getBlockWhenExhausted()} is true is
-     * determined by the value passed in to the {@code borrowMaxWaitMillis}
-     * parameter.
+     * determined by the value passed in to the {@code maxWaitMillis}
+     * parameter.  Passing a negative duration will cause this method to block
+     * indefinitely until an object becomes available.
      * </p>
      * <p>
      * When the pool is exhausted, multiple calling threads may be
@@ -407,15 +403,17 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * available instances in request arrival order.
      * </p>
      *
-     * @param borrowMaxWaitMillis The time to wait in milliseconds for an 
object
+     * @param maxWaitMillis The time to wait in milliseconds for an object
      *                            to become available
      * @return object instance from the pool
      * @throws NoSuchElementException if an instance cannot be returned
      * @throws Exception if an object instance cannot be returned due to an
      *                   error
+     * @deprecated Use {@link #borrowObject(Duration)}.
      */
-    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
-        return borrowObject(Duration.ofMillis(borrowMaxWaitMillis));
+    @Deprecated
+    public T borrowObject(final long maxWaitMillis) throws Exception {
+        return borrowObject(Duration.ofMillis(maxWaitMillis));
     }
 
     /**
@@ -495,13 +493,13 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * If the factory makeObject returns null, this method throws a 
NullPointerException.
      * </p>
      *
-     * @param maxWaitDuration The time to wait for an object to become 
available.
+     * @param maxWaitDurationRequest The time to wait for capacity to create.
      * @return The new wrapped pooled object or null.
-     * @throws Exception if the object factory's {@code makeObject} fails
+     * @throws Exception if the object factory's {@code makeObject} fails.
      */
-    private PooledObject<T> create(final Duration maxWaitDuration) throws 
Exception {
+    private PooledObject<T> create(final Duration maxWaitDurationRequest) 
throws Exception {
         final Instant startInstant = Instant.now();
-        Duration remainingWaitDuration = maxWaitDuration.isNegative() ? 
Duration.ZERO : maxWaitDuration;
+        final Duration maxWaitDuration = maxWaitDurationRequest.isNegative() ? 
Duration.ZERO : maxWaitDurationRequest;
         int localMaxTotal = getMaxTotal();
         // This simplifies the code later in this method
         if (localMaxTotal < 0) {
@@ -516,7 +514,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         Boolean create = null;
         while (create == null) {
             // remainingWaitDuration handles spurious wakeup from wait().
-            remainingWaitDuration = 
remainingWaitDuration.minus(durationSince(startInstant));
+            final Duration remainingWaitDuration = 
maxWaitDuration.minus(durationSince(startInstant));
             synchronized (makeObjectCountLock) {
                 final long newCreateCount = createCount.incrementAndGet();
                 if (newCreateCount > localMaxTotal) {
@@ -580,7 +578,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         }
 
         createdCount.incrementAndGet();
-        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
+        allObjects.put(IdentityWrapper.unwrap(p), p);
         return p;
     }
 
@@ -595,7 +593,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
     private void destroy(final PooledObject<T> toDestroy, final DestroyMode 
destroyMode) throws Exception {
         toDestroy.invalidate();
         idleObjects.remove(toDestroy);
-        allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
+        allObjects.remove(IdentityWrapper.unwrap(toDestroy));
         try {
             factory.destroyObject(toDestroy, destroyMode);
         } finally {
@@ -604,6 +602,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         }
     }
 
+
     /**
      * Tries to ensure that {@code idleCount} idle instances exist in the pool.
      * <p>
@@ -760,8 +759,6 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                             }
                         }
                         underTest.endEvictionTest(idleObjects);
-                        // TODO - May need to add code here once additional
-                        // states are used
                     }
                 }
             }
@@ -917,7 +914,8 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * {@inheritDoc}
      * <p>
      * Activation of this method decrements the active count and attempts to 
destroy the instance, using the provided
-     * {@link DestroyMode}.
+     * {@link DestroyMode}. To ensure liveness of the pool, {@link 
#addObject()} is called to replace the invalidated
+     * instance.
      * </p>
      *
      * @throws Exception if an exception occurs destroying the object
@@ -938,7 +936,9 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                 destroy(p, destroyMode);
             }
         }
-        ensureIdle(1, false);
+        if (!isClosed()) {
+            addObject();
+        }
     }
 
     /**
@@ -957,11 +957,12 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
     public Set<DefaultPooledObjectInfo> listAllObjects() {
         return 
allObjects.values().stream().map(DefaultPooledObjectInfo::new).collect(Collectors.toSet());
     }
+
     /**
      * Tries to ensure that {@link #getMinIdle()} idle instances are available
      * in the pool.
      *
-     * @throws Exception If the associated factory throws an exception
+     * @throws Exception If the associated factory throws an exception.
      * @since 2.4
      */
     public void preparePool() throws Exception {
@@ -973,7 +974,10 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
 
     /**
      * Recovers abandoned objects which have been checked out but
-     * not used since longer than the removeAbandonedTimeout.
+     * not used since longer than the removeAbandonedTimeout. For each object
+     * deemed abandoned, {@link #invalidateObject(Object)} is called. This
+     * results in the object being destroyed and then {@link #addObject()} is
+     * called to try to replace it.
      *
      * @param abandonedConfig The configuration to use to identify abandoned 
objects
      */
@@ -1021,75 +1025,76 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
             }
             return; // Object was abandoned and removed
         }
+        synchronized (p) {
+            markReturningState(p);
 
-        markReturningState(p);
-
-        final Duration activeTime = p.getActiveDuration();
+            final Duration activeTime = p.getActiveDuration();
 
-        if (getTestOnReturn() && !factory.validateObject(p)) {
-            try {
-                destroy(p, DestroyMode.NORMAL);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            try {
-                ensureIdle(1, false);
-            } catch (final Exception e) {
-                swallowException(e);
+            if (getTestOnReturn() && !factory.validateObject(p)) {
+                try {
+                    destroy(p, DestroyMode.NORMAL);
+                } catch (final Exception e) {
+                    swallowException(e);
+                }
+                try {
+                    ensureIdle(1, false);
+                } catch (final Exception e) {
+                    swallowException(e);
+                }
+                updateStatsReturn(activeTime);
+                return;
             }
-            updateStatsReturn(activeTime);
-            return;
-        }
 
-        try {
-            factory.passivateObject(p);
-        } catch (final Exception e1) {
-            swallowException(e1);
             try {
-                destroy(p, DestroyMode.NORMAL);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            try {
-                ensureIdle(1, false);
-            } catch (final Exception e) {
-                swallowException(e);
+                factory.passivateObject(p);
+            } catch (final Exception e1) {
+                swallowException(e1);
+                try {
+                    destroy(p, DestroyMode.NORMAL);
+                } catch (final Exception e) {
+                    swallowException(e);
+                }
+                try {
+                    ensureIdle(1, false);
+                } catch (final Exception e) {
+                    swallowException(e);
+                }
+                updateStatsReturn(activeTime);
+                return;
             }
-            updateStatsReturn(activeTime);
-            return;
-        }
-
-        if (!p.deallocate()) {
-            throw new IllegalStateException(
-                    "Object has already been returned to this pool or is 
invalid");
-        }
 
-        final int maxIdleSave = getMaxIdle();
-        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= 
idleObjects.size()) {
-            try {
-                destroy(p, DestroyMode.NORMAL);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            try {
-                ensureIdle(1, false);
-            } catch (final Exception e) {
-                swallowException(e);
+            if (!p.deallocate()) {
+                throw new IllegalStateException(
+                        "Object has already been returned to this pool or is 
invalid");
             }
-        } else {
-            if (getLifo()) {
-                idleObjects.addFirst(p);
+
+            final int maxIdleSave = getMaxIdle();
+            if (isClosed() || maxIdleSave > -1 && maxIdleSave <= 
idleObjects.size()) {
+                try {
+                    destroy(p, DestroyMode.NORMAL);
+                } catch (final Exception e) {
+                    swallowException(e);
+                }
+                try {
+                    ensureIdle(1, false);
+                } catch (final Exception e) {
+                    swallowException(e);
+                }
             } else {
-                idleObjects.addLast(p);
-            }
-            if (isClosed()) {
-                // Pool closed while object was being added to idle objects.
-                // Make sure the returned object is destroyed rather than left
-                // in the idle object pool (which would effectively be a leak)
-                clear();
+                if (getLifo()) {
+                    idleObjects.addFirst(p);
+                } else {
+                    idleObjects.addLast(p);
+                }
+                if (isClosed()) {
+                    // Pool closed while object was being added to idle 
objects.
+                    // Make sure the returned object is destroyed rather than 
left
+                    // in the idle object pool (which would effectively be a 
leak)
+                    clear();
+                }
             }
+            updateStatsReturn(activeTime);
         }
-        updateStatsReturn(activeTime);
     }
 
     /**
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java
index ec3271e879..157037ca13 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -57,6 +57,13 @@ public class GenericObjectPoolConfig<T> extends 
BaseObjectPoolConfig<T> {
 
     private int minIdle = DEFAULT_MIN_IDLE;
 
+    /**
+     * Constructs a new instance.
+     */
+    public GenericObjectPoolConfig() {
+        // empty
+    }
+
     @SuppressWarnings("unchecked")
     @Override
     public GenericObjectPoolConfig<T> clone() {
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java
index f17aa3ff6c..48a9a1ef89 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java
index 63be6e1678..227f5686cd 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -37,18 +37,18 @@ final class InterruptibleReentrantLock extends 
ReentrantLock {
      * Constructs a new InterruptibleReentrantLock with the given fairness 
policy.
      *
      * @param fairness true means threads should acquire contended locks as if
-     * waiting in a FIFO queue
+     * waiting in a FIFO queue.
      */
     InterruptibleReentrantLock(final boolean fairness) {
         super(fairness);
     }
 
     /**
-     * Interrupts the threads that are waiting on a specific condition
+     * Interrupts the threads that are waiting on a specific condition.
      *
      * @param condition the condition on which the threads are waiting.
      */
-    public void interruptWaiters(final Condition condition) {
+    void interruptWaiters(final Condition condition) {
         getWaitingThreads(condition).forEach(Thread::interrupt);
     }
 }
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java
index 7edd10f64e..441f203b9e 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -22,10 +22,10 @@ import java.io.Serializable;
 import java.time.Duration;
 import java.util.AbstractQueue;
 import java.util.Collection;
-import java.util.Deque;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.concurrent.BlockingDeque;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
 
@@ -66,7 +66,7 @@ import java.util.concurrent.locks.Condition;
  * @since 2.0
  */
 final class LinkedBlockingDeque<E> extends AbstractQueue<E>
-        implements Deque<E>, Serializable {
+        implements BlockingDeque<E>, Serializable {
 
     /*
      * Implemented as a simple doubly-linked list protected by a
@@ -223,18 +223,26 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
     /** Descending iterator */
     private final class DescendingItr extends AbstractItr {
         @Override
-        Node<E> firstNode() { return last; }
+        Node<E> firstNode() {
+            return last;
+        }
         @Override
-        Node<E> nextNode(final Node<E> n) { return n.prev; }
+        Node<E> nextNode(final Node<E> n) {
+            return n.prev;
+        }
     }
 
     /** Forward iterator */
     private final class Itr extends AbstractItr {
         @Override
-        Node<E> firstNode() { return first; }
+        Node<E> firstNode() {
+            return first;
+        }
         @Override
-        Node<E> nextNode(final Node<E> n) { return n.next; }
+        Node<E> nextNode(final Node<E> n) {
+            return n.next;
         }
+    }
 
     /**
      * Doubly-linked list node class.
@@ -471,6 +479,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws NullPointerException if c is null
      * @throws IllegalArgumentException if c is this instance
      */
+    @Override
     public int drainTo(final Collection<? super E> c) {
         return drainTo(c, Integer.MAX_VALUE);
     }
@@ -490,6 +499,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws NullPointerException if c is null
      * @throws IllegalArgumentException if c is this instance
      */
+    @Override
     public int drainTo(final Collection<? super E> collection, final int 
maxElements) {
         Objects.requireNonNull(collection, "collection");
         if (collection == this) {
@@ -547,7 +557,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      *
      * @return number of threads waiting on this deque's notEmpty condition.
      */
-    public int getTakeQueueLength() {
+    int getTakeQueueLength() {
         lock.lock();
         try {
            return lock.getWaitQueueLength(notEmpty);
@@ -562,7 +572,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      *
      * @return true if there is at least one thread waiting on this deque's 
notEmpty condition.
      */
-    public boolean hasTakeWaiters() {
+    boolean hasTakeWaiters() {
         lock.lock();
         try {
             return lock.hasWaiters(notEmpty);
@@ -575,7 +585,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * Interrupts the threads currently waiting to take an object from the 
pool. See disclaimer on accuracy in
      * {@link 
java.util.concurrent.locks.ReentrantLock#getWaitingThreads(Condition)}.
      */
-    public void interuptTakeWaiters() {
+    void interuptTakeWaiters() {
         lock.lock();
         try {
             lock.interruptWaiters(notEmpty);
@@ -685,6 +695,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
+    @Override
     public boolean offer(final E e, final long timeout, final TimeUnit unit) 
throws InterruptedException {
         return offerLast(e, timeout, unit);
     }
@@ -711,7 +722,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
-    public boolean offerFirst(final E e, final Duration timeout) throws 
InterruptedException {
+    boolean offerFirst(final E e, final Duration timeout) throws 
InterruptedException {
         Objects.requireNonNull(e, "e");
         long nanos = timeout.toNanos();
         lock.lockInterruptibly();
@@ -740,6 +751,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
+    @Override
     public boolean offerFirst(final E e, final long timeout, final TimeUnit 
unit) throws InterruptedException {
         return offerFirst(e, PoolImplUtils.toDuration(timeout, unit));
     }
@@ -795,6 +807,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whist waiting
      *         for space
      */
+    @Override
     public boolean offerLast(final E e, final long timeout, final TimeUnit 
unit) throws InterruptedException {
         return offerLast(e, PoolImplUtils.toDuration(timeout, unit));
     }
@@ -856,6 +869,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
+    @Override
     public E poll(final long timeout, final TimeUnit unit) throws 
InterruptedException {
         return pollFirst(timeout, unit);
     }
@@ -904,6 +918,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
+    @Override
     public E pollFirst(final long timeout, final TimeUnit unit) throws 
InterruptedException {
         return pollFirst(PoolImplUtils.toDuration(timeout, unit));
     }
@@ -926,7 +941,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
-    public E pollLast(final Duration timeout)
+    E pollLast(final Duration timeout)
         throws InterruptedException {
         long nanos = timeout.toNanos();
         lock.lockInterruptibly();
@@ -953,6 +968,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
+    @Override
     public E pollLast(final long timeout, final TimeUnit unit)
         throws InterruptedException {
         return pollLast(PoolImplUtils.toDuration(timeout, unit));
@@ -981,6 +997,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
+    @Override
     public void put(final E e) throws InterruptedException {
         putLast(e);
     }
@@ -994,6 +1011,7 @@ final class LinkedBlockingDeque<E> extends AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
+    @Override
     public void putFirst(final E e) throws InterruptedException {
         Objects.requireNonNull(e, "e");
         lock.lock();
@@ -1015,6 +1033,7 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
      * @throws InterruptedException if the thread is interrupted whilst waiting
      *         for space
      */
+    @Override
     public void putLast(final E e) throws InterruptedException {
         Objects.requireNonNull(e, "e");
         lock.lock();
@@ -1030,12 +1049,11 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
     // Stack methods
 
     /**
-     * Reconstitutes this deque from a stream (that is,
-     * deserialize it).
+     * Reconstitutes this deque from a stream (that is, deserialize it).
+     *
      * @param s the stream
      */
-    private void readObject(final ObjectInputStream s)
-        throws IOException, ClassNotFoundException {
+    private void readObject(final ObjectInputStream s) throws IOException, 
ClassNotFoundException {
         s.defaultReadObject();
         count = 0;
         first = null;
@@ -1043,7 +1061,7 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
         // Read in all elements and place in queue
         for (;;) {
             @SuppressWarnings("unchecked")
-            final E item = (E)s.readObject();
+            final E item = (E) s.readObject();
             if (item == null) {
                 break;
             }
@@ -1066,6 +1084,7 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
      *
      * @return The number of additional elements the queue is able to accept
      */
+    @Override
     public int remainingCapacity() {
         lock.lock();
         try {
@@ -1238,6 +1257,7 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
+    @Override
     public E take() throws InterruptedException {
         return takeFirst();
     }
@@ -1249,6 +1269,7 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
+    @Override
     public E takeFirst() throws InterruptedException {
         lock.lock();
         try {
@@ -1269,6 +1290,7 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
      * @return the unlinked element
      * @throws InterruptedException if the current thread is interrupted
      */
+    @Override
     public E takeLast() throws InterruptedException {
         lock.lock();
         try {
@@ -1319,12 +1341,11 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
         lock.lock();
         try {
             if (a.length < count) {
-                a = (T[])java.lang.reflect.Array.newInstance
-                    (a.getClass().getComponentType(), count);
+                a = (T[]) 
java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), count);
             }
             int k = 0;
             for (Node<E> p = first; p != null; p = p.next) {
-                a[k++] = (T)p.item;
+                a[k++] = (T) p.item;
             }
             if (a.length > k) {
                 a[k] = null;
@@ -1362,9 +1383,8 @@ final class LinkedBlockingDeque<E> extends 
AbstractQueue<E>
             p.next = n;
             n.prev = p;
             x.item = null;
-            // Don't mess with x's links.  They may still be in use by
-            // an iterator.
-        --count;
+            // Don't mess with x's links.  They may still be in use by an 
iterator.
+            --count;
             notFull.signal();
         }
     }
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java
index 24dcac6ca9..172c3550ab 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -27,7 +27,7 @@ import java.io.PrintWriter;
 public class NoOpCallStack implements CallStack {
 
     /**
-     * Singleton instance.
+     * The singleton instance.
      */
     public static final CallStack INSTANCE = new NoOpCallStack();
 
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java
index f552f38bc9..a36a22c7e1 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java
index 68f2fcacca..31282bb193 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
diff --git 
a/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
index b64597fda2..c136903819 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.Optional;
+import java.util.concurrent.BlockingDeque;
 
 import org.apache.tomcat.dbcp.pool2.BaseObjectPool;
 import org.apache.tomcat.dbcp.pool2.ObjectPool;
@@ -62,8 +63,7 @@ public class SoftReferenceObjectPool<T> extends 
BaseObjectPool<T> {
     private long createCount; // @GuardedBy("this")
 
     /** Idle references - waiting to be borrowed */
-    private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences =
-        new LinkedBlockingDeque<>();
+    private final BlockingDeque<PooledSoftReference<T>> idleReferences = new 
LinkedBlockingDeque<>();
 
     /** All references - checked out or waiting to be borrowed. */
     private final ArrayList<PooledSoftReference<T>> allReferences =
@@ -262,7 +262,7 @@ public class SoftReferenceObjectPool<T> extends 
BaseObjectPool<T> {
     }
 
     /**
-     * Destroys a {@code PooledSoftReference} and removes it from the idle and 
all
+     * Destroys a {@link PooledSoftReference} and removes it from the idle and 
all
      * references pools.
      *
      * @param toDestroy PooledSoftReference to destroy
diff --git a/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java 
b/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java
index dc63408442..5b7d6ddf60 100644
--- a/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java
+++ b/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java
@@ -6,7 +6,7 @@
  * (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
+ *      https://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,
@@ -42,7 +42,7 @@ public class ThrowableCallStack implements CallStack {
         /**
          * Constructs a new instance with its message set to the now instant.
          */
-        Snapshot() {
+        private Snapshot() {
             this(Instant.now());
         }
 
diff --git a/res/checkstyle/header-al2.txt b/res/checkstyle/header-al2.txt
index 723479356f..0619032075 100644
--- a/res/checkstyle/header-al2.txt
+++ b/res/checkstyle/header-al2.txt
@@ -9,7 +9,7 @@
 ^(rem)?\W*\(the "License"\); you may not use this file except in compliance 
with$
 ^(rem)?\W*the License\.  You may obtain a copy of the License at$
 ^(rem)?\W*$
-^(rem)?\W*http://www.apache.org/licenses/LICENSE-2\.0$
+^(rem)?\W*http(s)?://www.apache.org/licenses/LICENSE-2\.0$
 ^(rem)?\W*$
 ^(rem)?\W*Unless required by applicable law or agreed to in writing, software$
 ^(rem)?\W*distributed under the License is distributed on an "AS IS" BASIS,$
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 37f2733bac..90519f3c4b 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -121,6 +121,9 @@
         during test execution. Useful for cleaner console output when running
         tests with multiple threads. (csutherl)
       </add>
+      <update>
+        Update the internal fork of Commons Pool to 2.13.0. (markt)
+      </update>
     </changelog>
   </subsection>
   <subsection name="Catalina">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to