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]
