This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git
The following commit(s) were added to refs/heads/master by this push:
new 26b0829 [POOL-395] Improve exception thrown in
GenericObjectPool.borrowObject when pool is exhausted. Added
BaseGenericObjectPool.setMessagesStatistics(boolean).
26b0829 is described below
commit 26b0829fef39729df84adb1c01eb55c944aff6d7
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Sun Jun 27 10:47:03 2021 -0400
[POOL-395] Improve exception thrown in GenericObjectPool.borrowObject
when pool is exhausted. Added
BaseGenericObjectPool.setMessagesStatistics(boolean).
- Pick up Checkstyle LineLength module from Commons IO where there was
none before.
- Message sentences start with a capital letter.
- Better parameter names.
- Javadoc.
- Inline comments.
---
checkstyle.xml | 4 +
src/changes/changes.xml | 3 +
.../org/apache/commons/pool2/KeyedObjectPool.java | 4 +-
.../commons/pool2/KeyedPooledObjectFactory.java | 4 +-
.../java/org/apache/commons/pool2/ObjectPool.java | 4 +-
.../apache/commons/pool2/PooledObjectFactory.java | 4 +-
.../commons/pool2/impl/BaseGenericObjectPool.java | 114 ++++++++++++++++-----
.../commons/pool2/impl/GenericKeyedObjectPool.java | 48 +++++----
.../commons/pool2/impl/GenericObjectPool.java | 46 +++++----
.../pool2/impl/SoftReferenceObjectPool.java | 4 +-
.../pool2/TestBasePoolableObjectFactory.java | 4 +-
.../pool2/impl/TestAbandonedKeyedObjectPool.java | 4 +-
.../pool2/impl/TestAbandonedObjectPool.java | 4 +-
.../pool2/impl/TestGenericKeyedObjectPool.java | 64 +++++++-----
.../commons/pool2/impl/TestGenericObjectPool.java | 28 ++++-
15 files changed, 230 insertions(+), 109 deletions(-)
diff --git a/checkstyle.xml b/checkstyle.xml
index dca770b..3f884e1 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -74,4 +74,8 @@
<property name="allowLegacy" value="true"/>
</module>
+ <module name="LineLength">
+ <property name="max" value="160"/>
+ </module>
+
</module>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 40ef624..2368098 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -69,6 +69,9 @@ The <action> type attribute can be add,update,fix,remove.
<action issue="POOL-396" dev="ggregory" type="add" due-to="Jeremy Kong, Phil
Steitz">
Handle validation exceptions during eviction. #85.
</action>
+ <action issue="POOL-395" dev="ggregory" type="add" due-to="Gary Gregory, Arash
Nikoo">
+ Improve exception thrown in GenericObjectPool.borrowObject when pool is
exhausted. Added BaseGenericObjectPool.setMessagesStatistics(boolean).
+ </action>
<!-- FIXES -->
<action dev="ggregory" type="fix" due-to="Gary Gregory">
Fix [WARNING] Old version of checkstyle detected. Consider updating to
>= v8.30. Update Checktyle to 8.43.
diff --git a/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
b/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
index ab52908..cac55ac 100644
--- a/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
@@ -289,12 +289,12 @@ public interface KeyedObjectPool<K, V> extends Closeable {
*
* @param key the key used to obtain the object
* @param obj a {@link #borrowObject borrowed} instance to be returned.
- * @param mode destroy activation context provided to the factory
+ * @param destroyMode destroy activation context provided to the factory
*
* @throws Exception if the instance cannot be invalidated
* @since 2.9.0
*/
- default void invalidateObject(final K key, final V obj, final DestroyMode
mode) throws Exception {
+ default void invalidateObject(final K key, final V obj, final DestroyMode
destroyMode) throws Exception {
invalidateObject(key, obj);
}
diff --git a/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java b/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java
index ac93114..a7f75a3 100644
--- a/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java
+++ b/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java
@@ -117,7 +117,7 @@ public interface KeyedPooledObjectFactory<K, V> {
*
* @param key the key used when selecting the instance
* @param p a {@code PooledObject} wrapping the instance to be destroyed
- * @param mode DestroyMode providing context to the factory
+ * @param destroyMode DestroyMode providing context to the factory
*
* @throws Exception should be avoided as it may be swallowed by
* the pool implementation.
@@ -128,7 +128,7 @@ public interface KeyedPooledObjectFactory<K, V> {
* @see DestroyMode
* @since 2.9.0
*/
- default void destroyObject(final K key, final PooledObject<V> p, final
DestroyMode mode) throws Exception {
+ default void destroyObject(final K key, final PooledObject<V> p, final
DestroyMode destroyMode) throws Exception {
destroyObject(key, p);
}
diff --git a/src/main/java/org/apache/commons/pool2/ObjectPool.java b/src/main/java/org/apache/commons/pool2/ObjectPool.java
index 8225d27..8aa2d58 100644
--- a/src/main/java/org/apache/commons/pool2/ObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/ObjectPool.java
@@ -198,12 +198,12 @@ public interface ObjectPool<T> extends Closeable {
* </p>
*
* @param obj a {@link #borrowObject borrowed} instance to be disposed.
- * @param mode destroy activation context provided to the factory
+ * @param destroyMode destroy activation context provided to the factory
*
* @throws Exception if the instance cannot be invalidated
* @since 2.9.0
*/
- default void invalidateObject(final T obj, final DestroyMode mode) throws
Exception {
+ default void invalidateObject(final T obj, final DestroyMode destroyMode)
throws Exception {
invalidateObject(obj);
}
diff --git a/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java b/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java
index 009f341..839e866 100644
--- a/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java
+++ b/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java
@@ -112,7 +112,7 @@ public interface PooledObjectFactory<T> {
* DestroyMode.
*
* @param p a {@code PooledObject} wrapping the instance to be destroyed
- * @param mode DestroyMode providing context to the factory
+ * @param destroyMode DestroyMode providing context to the factory
*
* @throws Exception should be avoided as it may be swallowed by
* the pool implementation.
@@ -123,7 +123,7 @@ public interface PooledObjectFactory<T> {
* @see DestroyMode
* @since 2.9.0
*/
- default void destroyObject(final PooledObject<T> p, final DestroyMode mode)
throws Exception {
+ default void destroyObject(final PooledObject<T> p, final DestroyMode
destroyMode) throws Exception {
destroyObject(p);
}
diff --git a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
index 000a6eb..83f37e1 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
@@ -26,9 +26,11 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
+import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
@@ -233,12 +235,14 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
return builder.toString();
}
}
+
/**
* Maintains a cache of values for a single metric and reports
* statistics on the cached values.
*/
private class StatsStore {
+ private static final int NULL = -1;
private final AtomicLong[] values;
private final int size;
private int index;
@@ -252,7 +256,7 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
this.size = size;
values = new AtomicLong[size];
for (int i = 0; i < size; i++) {
- values[i] = new AtomicLong(-1);
+ values[i] = new AtomicLong(NULL);
}
}
@@ -275,6 +279,15 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
}
/**
+ * Gets the current values as a List.
+ *
+ * @return the current values as a List.
+ */
+ synchronized List<AtomicLong> getCurrentValues() {
+ return Arrays.stream(values, 0,
index).collect(Collectors.toList());
+ }
+
+ /**
* Gets the mean of the cached values.
*
* @return the mean of the cache, truncated to long
@@ -284,10 +297,9 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
int counter = 0;
for (int i = 0; i < size; i++) {
final long value = values[i].get();
- if (value != -1) {
+ if (value != NULL) {
counter++;
- result = result * ((counter - 1) / (double) counter) +
- value/(double) counter;
+ result = result * ((counter - 1) / (double) counter) +
value / (double) counter;
}
}
return (long) result;
@@ -296,16 +308,19 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
- builder.append("StatsStore [values=");
- builder.append(Arrays.toString(values));
- builder.append(", size=");
+ builder.append("StatsStore [");
+ // Only append what's been filled in.
+ builder.append(getCurrentValues());
+ builder.append("], size=");
builder.append(size);
builder.append(", index=");
builder.append(index);
builder.append("]");
return builder.toString();
}
+
}
+
// Constants
/**
* The size of the caches used to store historical data for some
attributes
@@ -334,7 +349,6 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
final Object closeLock = new Object();
volatile boolean closed;
-
final Object evictionLock = new Object();
private Evictor evictor = null; // @GuardedBy("evictionLock")
EvictionIterator evictionIterator = null; // @GuardedBy("evictionLock")
@@ -348,21 +362,21 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
// Monitoring (primarily JMX) attributes
private final ObjectName objectName;
private final String creationStackTrace;
- private final AtomicLong borrowedCount = new AtomicLong(0);
- private final AtomicLong returnedCount = new AtomicLong(0);
- final AtomicLong createdCount = new AtomicLong(0);
- final AtomicLong destroyedCount = new AtomicLong(0);
- final AtomicLong destroyedByEvictorCount = new AtomicLong(0);
- final AtomicLong destroyedByBorrowValidationCount = new AtomicLong(0);
+ private final AtomicLong borrowedCount = new AtomicLong();
+ private final AtomicLong returnedCount = new AtomicLong();
+ final AtomicLong createdCount = new AtomicLong();
+ final AtomicLong destroyedCount = new AtomicLong();
+ final AtomicLong destroyedByEvictorCount = new AtomicLong();
+ final AtomicLong destroyedByBorrowValidationCount = new AtomicLong();
+
private final StatsStore activeTimes = new
StatsStore(MEAN_TIMING_STATS_CACHE_SIZE);
-
private final StatsStore idleTimes = new
StatsStore(MEAN_TIMING_STATS_CACHE_SIZE);
-
private final StatsStore waitTimes = new
StatsStore(MEAN_TIMING_STATS_CACHE_SIZE);
- private final AtomicLong maxBorrowWaitTimeMillis = new AtomicLong(0L);
+ private final AtomicLong maxBorrowWaitTimeMillis = new AtomicLong();
- private volatile SwallowedExceptionListener swallowedExceptionListener = null;
+ private volatile SwallowedExceptionListener swallowedExceptionListener;
+ private volatile boolean messageStatistics;
/**
* Handles JMX registration (if required) and the initialization required
for
@@ -396,6 +410,16 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
}
/**
+ * Appends statistics if enabled.
+ *
+ * @param string The root string.
+ * @return The root string plus statistics.
+ */
+ String appendStats(final String string) {
+ return messageStatistics ? string + ", " + getStatsString() : string;
+ }
+
+ /**
* Verifies that the pool is open.
* @throws IllegalStateException if the pool is closed.
*/
@@ -680,6 +704,16 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
}
/**
+ * Gets whether to include statistics in exception messages.
+ *
+ * @return whether to include statistics in exception messages.
+ * @since 2.11.0
+ */
+ public boolean getMessageStatistics() {
+ return messageStatistics;
+ }
+
+ /**
* Gets the minimum amount of time an object may sit idle in the pool
* before it is eligible for eviction by the idle object evictor (if any -
* see {@link #setTimeBetweenEvictionRuns(Duration)}). When non-positive,
@@ -806,6 +840,22 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
}
/**
+ * Gets a statistics string.
+ *
+ * @return a statistics string.
+ */
+ String getStatsString() {
+ // Simply listed in AB order.
+ return String.format(
+ "activeTimes=%s, blockWhenExhausted=%s, borrowedCount=%,d,
closed=%s, createdCount=%,d, destroyedByBorrowValidationCount=%,d,
destroyedByEvictorCount=%,d, evictorShutdownTimeout=%s, fairness=%s, idleTimes=%s,
lifo=%s, maxBorrowWaitTimeMillis=%,d, maxTotal=%s, maxWaitDuration=%s,
minEvictableIdleTime=%s, numTestsPerEvictionRun=%s, returnedCount=%s,
softMinEvictableIdleTime=%s, testOnBorrow=%s, testOnCreate=%s, testOnReturn=%s,
testWhileIdle=%s, timeBetweenEvictionRuns=%s, [...]
+ activeTimes.getCurrentValues(), blockWhenExhausted,
borrowedCount.get(), closed, createdCount.get(),
+ destroyedByBorrowValidationCount.get(),
destroyedByEvictorCount.get(), evictorShutdownTimeout, fairness,
+ idleTimes.getCurrentValues(), lifo,
maxBorrowWaitTimeMillis.get(), maxTotal, maxWaitDuration,
+ minEvictableIdleTime, numTestsPerEvictionRun, returnedCount,
softMinEvictableIdleTime, testOnBorrow,
+ testOnCreate, testOnReturn, testWhileIdle,
timeBetweenEvictionRuns, waitTimes.getCurrentValues());
+ }
+
+ /**
* The listener used (if any) to receive notifications of exceptions
* unavoidably swallowed by the pool.
*
@@ -984,8 +1034,7 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
final void jmxUnregister() {
if (objectName != null) {
try {
- ManagementFactory.getPlatformMBeanServer().unregisterMBean(
- objectName);
+
ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName);
} catch (final MBeanRegistrationException |
InstanceNotFoundException e) {
swallowException(e);
}
@@ -997,10 +1046,9 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
* @param pooledObject instance to return to the keyed pool
*/
protected void markReturningState(final PooledObject<T> pooledObject) {
- synchronized(pooledObject) {
+ synchronized (pooledObject) {
if (pooledObject.getState() != PooledObjectState.ALLOCATED) {
- throw new IllegalStateException(
- "Object has already been returned to this pool or is
invalid");
+ throw new IllegalStateException("Object has already been returned
to this pool or is invalid");
}
pooledObject.markReturning(); // Keep from being marked abandoned
}
@@ -1121,9 +1169,9 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
classLoader + ", " + epClassLoader + "] do not implement
" + EVICTION_POLICY_TYPE_NAME);
} catch (final ClassNotFoundException | InstantiationException |
IllegalAccessException |
InvocationTargetException | NoSuchMethodException e) {
- final String exMessage = "Unable to create " + EVICTION_POLICY_TYPE_NAME +
" instance of type " +
- evictionPolicyClassName;
- throw new IllegalArgumentException(exMessage, e);
+ throw new IllegalArgumentException(
+ "Unable to create " + EVICTION_POLICY_TYPE_NAME + " instance of
type " + evictionPolicyClassName,
+ e);
}
}
@@ -1223,6 +1271,16 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
}
/**
+ * Sets whether to include statistics in exception messages.
+ *
+ * @param messagesDetails whether to include statistics in exception
messages.
+ * @since 2.11.0
+ */
+ public void setMessagesStatistics(boolean messagesDetails) {
+ this.messageStatistics = messagesDetails;
+ }
+
+ /**
* Sets the minimum amount of time an object may sit idle in the pool
* before it is eligible for eviction by the idle object evictor (if any -
* see {@link #setTimeBetweenEvictionRuns(Duration)}). When non-positive,
@@ -1446,6 +1504,8 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
}
+ // Inner classes
+
/**
* <p>Starts the evictor with the given delay. If there is an evictor
* running when this method is called, it is stopped and replaced with a
@@ -1478,8 +1538,6 @@ public abstract class BaseGenericObjectPool<T> extends
BaseObject {
}
}
- // Inner classes
-
/**
* Stops the evictor.
*/
diff --git
a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
index 11fd633..64274bd 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -121,7 +121,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
* Invariant: empty keyed pool will not be dropped unless
numInterested
* is 0.
*/
- private final AtomicLong numInterested = new AtomicLong(0);
+ private final AtomicLong numInterested = new AtomicLong();
/**
* Constructs a new ObjecDeque with the given fairness policy.
@@ -267,7 +267,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
if (factory == null) {
jmxUnregister(); // tidy up
- throw new IllegalArgumentException("factory may not be null");
+ throw new IllegalArgumentException("Factory may not be null");
}
this.factory = factory;
this.fairness = config.getFairness();
@@ -449,16 +449,15 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
if (borrowMaxWaitMillis < 0) {
p = objectDeque.getIdleObjects().takeFirst();
} else {
- p = objectDeque.getIdleObjects().pollFirst(
- borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
+ p =
objectDeque.getIdleObjects().pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
if (p == null) {
- throw new NoSuchElementException(
- "Timeout waiting for idle object");
+ throw new NoSuchElementException(appendStats(
+ "Timeout waiting for idle object,
borrowMaxWaitMillis=" + borrowMaxWaitMillis));
}
} else if (p == null) {
- throw new NoSuchElementException("Pool exhausted");
+ throw new NoSuchElementException(appendStats("Pool
exhausted"));
}
if (!p.allocate()) {
p = null;
@@ -475,8 +474,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
}
p = null;
if (create) {
- final NoSuchElementException nsee = new
NoSuchElementException(
- "Unable to activate object");
+ final NoSuchElementException nsee = new
NoSuchElementException(appendStats("Unable to activate object"));
nsee.initCause(e);
throw nsee;
}
@@ -500,7 +498,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
p = null;
if (create) {
final NoSuchElementException nsee = new
NoSuchElementException(
- "Unable to validate object");
+ appendStats("Unable to validate
object"));
nsee.initCause(validationThrowable);
throw nsee;
}
@@ -517,6 +515,13 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
return p.getObject();
}
+ @Override
+ String getStatsString() {
+ // Simply listed in AB order.
+ return super.getStatsString() +
+ String.format(", fairness=%s, maxIdlePerKey%,d, maxTotalPerKey=%,d,
minIdlePerKey=%,d, numTotal=%,d",
+ fairness, maxIdlePerKey, maxTotalPerKey,
minIdlePerKey, numTotal.get());
+ }
/**
* Calculate the number of objects that need to be created to attempt to
@@ -820,7 +825,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
/**
* De-register the use of a key by an object.
* <p>
- * register() and deregister() must always be used as a pair.
+ * {@link #register()} and {@link #deregister()} must always be used as a
pair.
* </p>
*
* @param k The key to de-register
@@ -858,12 +863,12 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
* @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 mode DestroyMode context provided to the factory
+ * @param destroyMode DestroyMode context provided to the factory
*
* @return {@code true} if the object was destroyed, otherwise {@code
false}
* @throws Exception If the object destruction failed
*/
- private boolean destroy(final K key, final PooledObject<T> toDestroy,
final boolean always, final DestroyMode mode)
+ private boolean destroy(final K key, final PooledObject<T> toDestroy,
final boolean always, final DestroyMode destroyMode)
throws Exception {
final ObjectDeque<T> objectDeque = register(key);
@@ -884,7 +889,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
toDestroy.invalidate();
try {
- factory.destroyObject(key, toDestroy, mode);
+ factory.destroyObject(key, toDestroy, destroyMode);
} finally {
objectDeque.getCreateCount().decrementAndGet();
destroyedCount.incrementAndGet();
@@ -1414,7 +1419,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
*
* @param key pool key
* @param obj instance to invalidate
- * @param mode DestroyMode context provided to factory
+ * @param destroyMode DestroyMode context provided to factory
*
* @throws Exception if an exception occurs destroying the
* object
@@ -1423,15 +1428,15 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
* @since 2.9.0
*/
@Override
- public void invalidateObject(final K key, final T obj, final DestroyMode
mode) throws Exception {
+ public void invalidateObject(final K key, final T obj, final DestroyMode
destroyMode) throws Exception {
final ObjectDeque<T> objectDeque = poolMap.get(key);
final PooledObject<T> p = objectDeque.getAllObjects().get(new
IdentityWrapper<>(obj));
if (p == null) {
- throw new IllegalStateException("Object not currently part of this
pool");
+ throw new IllegalStateException(appendStats("Object not currently part
of this pool"));
}
synchronized (p) {
if (p.getState() != PooledObjectState.INVALID) {
- destroy(key, p, true, mode);
+ destroy(key, p, true, destroyMode);
}
}
if (objectDeque.idleObjects.hasTakeWaiters()) {
@@ -1502,7 +1507,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
/**
* Register the use of a key by an object.
* <p>
- * register() and deregister() must always be used as a pair.
+ * {@link #register()} and {@link #deregister()} must always be used as a
pair.
* </p>
*
* @param k The key to register
@@ -1579,6 +1584,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
try {
invalidateObject(pool.getKey(), pooledObject.getObject(),
DestroyMode.ABANDONED);
} catch (final Exception e) {
+ // TODO handle/log?
e.printStackTrace();
}
}
@@ -1828,9 +1834,9 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
*
* @param minIdlePerKey The minimum size of the each keyed pool
*
- * @see #getMinIdlePerKey
+ * @see #getMinIdlePerKey()
* @see #getMaxIdlePerKey()
- * @see #setTimeBetweenEvictionRunsMillis
+ * @see #setTimeBetweenEvictionRuns(Duration)
*/
public void setMinIdlePerKey(final int minIdlePerKey) {
this.minIdlePerKey = minIdlePerKey;
diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
index ee27b58..c5fed24 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -115,7 +115,7 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
* {@link #create()} will ensure that there are never more than
* {@link #_maxActive} objects created at any one time.
*/
- private final AtomicLong createCount = new AtomicLong(0);
+ private final AtomicLong createCount = new AtomicLong();
private long makeObjectCount;
@@ -155,7 +155,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
if (factory == null) {
jmxUnregister(); // tidy up
- throw new IllegalArgumentException("factory may not be null");
+ throw new IllegalArgumentException("Factory may not be null");
}
this.factory = factory;
@@ -214,8 +214,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
public void addObject() throws Exception {
assertOpen();
if (factory == null) {
- throw new IllegalStateException(
- "Cannot add objects without a factory.");
+ throw new IllegalStateException("Cannot add objects without a
factory.");
}
final PooledObject<T> p = create();
addIdleObject(p);
@@ -271,7 +270,7 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
* available instances in request arrival order.
* </p>
*
- * @param borrowMaxWait The time to wait for an object
+ * @param borrowMaxWaitDuration The time to wait for an object
* to become available
*
* @return object instance from the pool
@@ -282,11 +281,11 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
* error
* @since 2.10.0
*/
- public T borrowObject(final Duration borrowMaxWait) throws Exception {
+ public T borrowObject(final Duration borrowMaxWaitDuration) throws
Exception {
assertOpen();
final AbandonedConfig ac = this.abandonedConfig;
- if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2)
&&
+ if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2)
&&
(getNumActive() > getMaxTotal() - 3)) {
removeAbandoned(ac);
}
@@ -311,17 +310,18 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
}
if (blockWhenExhausted) {
if (p == null) {
- if (borrowMaxWait.isNegative()) {
+ if (borrowMaxWaitDuration.isNegative()) {
p = idleObjects.takeFirst();
} else {
- p = idleObjects.pollFirst(borrowMaxWait);
+ p = idleObjects.pollFirst(borrowMaxWaitDuration);
}
}
if (p == null) {
- throw new NoSuchElementException("Timeout waiting for idle
object");
+ throw new NoSuchElementException(appendStats(
+ "Timeout waiting for idle object,
borrowMaxWaitMillis=" + borrowMaxWaitDuration));
}
} else if (p == null) {
- throw new NoSuchElementException("Pool exhausted");
+ throw new NoSuchElementException(appendStats("Pool
exhausted"));
}
if (!p.allocate()) {
p = null;
@@ -338,7 +338,8 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
}
p = null;
if (create) {
- final NoSuchElementException nsee = new
NoSuchElementException("Unable to activate object");
+ final NoSuchElementException nsee = new
NoSuchElementException(
+ appendStats("Unable to activate object"));
nsee.initCause(e);
throw nsee;
}
@@ -361,7 +362,8 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
}
p = null;
if (create) {
- final NoSuchElementException nsee = new
NoSuchElementException("Unable to validate object");
+ final NoSuchElementException nsee = new
NoSuchElementException(
+ appendStats("Unable to validate object"));
nsee.initCause(validationThrowable);
throw nsee;
}
@@ -375,6 +377,14 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
return p.getObject();
}
+ @Override
+ String getStatsString() {
+ // Simply listed in AB order.
+ return super.getStatsString() +
+ String.format(", createdCount=%,d, makeObjectCount=%,d,
maxIdle=%,d, minIdle=%,d",
+ createdCount.get(), makeObjectCount, maxIdle, minIdle);
+ }
+
/**
* Borrows an object from the pool using the specific waiting time which
only
* applies if {@link #getBlockWhenExhausted()} is true.
@@ -592,17 +602,17 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
* Destroys a wrapped pooled object.
*
* @param toDestroy The wrapped pooled object to destroy
- * @param mode DestroyMode context provided to the factory
+ * @param destroyMode DestroyMode context provided to the factory
*
* @throws Exception If the factory fails to destroy the pooled object
* cleanly
*/
- private void destroy(final PooledObject<T> toDestroy, final DestroyMode
mode) throws Exception {
+ private void destroy(final PooledObject<T> toDestroy, final DestroyMode
destroyMode) throws Exception {
toDestroy.invalidate();
idleObjects.remove(toDestroy);
allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
try {
- factory.destroyObject(toDestroy, mode);
+ factory.destroyObject(toDestroy, destroyMode);
} finally {
destroyedCount.incrementAndGet();
createCount.decrementAndGet();
@@ -1002,7 +1012,7 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
* @since 2.9.0
*/
@Override
- public void invalidateObject(final T obj, final DestroyMode mode) throws
Exception {
+ public void invalidateObject(final T obj, final DestroyMode destroyMode)
throws Exception {
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
if (p == null) {
if (isAbandonedConfig()) {
@@ -1013,7 +1023,7 @@ public class GenericObjectPool<T> extends
BaseGenericObjectPool<T>
}
synchronized (p) {
if (p.getState() != PooledObjectState.INVALID) {
- destroy(p, mode);
+ destroy(p, destroyMode);
}
}
ensureIdle(1, false);
diff --git
a/src/main/java/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java
b/src/main/java/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java
index 849629f..f7c271c 100644
--- a/src/main/java/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java
@@ -216,9 +216,7 @@ public class SoftReferenceObjectPool<T> extends
BaseObjectPool<T> {
obj = null;
}
if (newlyCreated) {
- throw new NoSuchElementException(
- "Could not create a validated object, cause: "
+
- t.getMessage());
+ throw new NoSuchElementException("Could not create a
validated object, cause: " + t);
}
}
}
diff --git
a/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
b/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
index 97c411e..43935d5 100644
--- a/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
+++ b/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
@@ -34,8 +34,8 @@ public class TestBasePoolableObjectFactory {
return new AtomicInteger(0);
}
@Override
- public void destroyObject(final PooledObject<AtomicInteger> p, final
DestroyMode mode){
- if (mode.equals(DestroyMode.ABANDONED)) {
+ public void destroyObject(final PooledObject<AtomicInteger> p, final
DestroyMode destroyMode){
+ if (destroyMode.equals(DestroyMode.ABANDONED)) {
p.getObject().incrementAndGet();
}
}
diff --git
a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedKeyedObjectPool.java
b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedKeyedObjectPool.java
index 541e1ea..0d850c9 100644
---
a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedKeyedObjectPool.java
+++
b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedKeyedObjectPool.java
@@ -103,7 +103,7 @@ public class TestAbandonedKeyedObjectPool {
}
@Override
- public void destroyObject(final Integer key, final
PooledObject<PooledTestObject> obj, final DestroyMode mode) throws Exception {
+ public void destroyObject(final Integer key, final
PooledObject<PooledTestObject> obj, final DestroyMode destroyMode) throws
Exception {
obj.getObject().setActive(false);
// while destroying instances, yield control to other threads
// helps simulate threading errors
@@ -111,7 +111,7 @@ public class TestAbandonedKeyedObjectPool {
if (destroyLatency != 0) {
Thread.sleep(destroyLatency);
}
- obj.getObject().destroy(mode);
+ obj.getObject().destroy(destroyMode);
}
@Override
diff --git
a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
index 14b264c..9b64560 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
@@ -183,7 +183,7 @@ public class TestAbandonedObjectPool {
}
@Override
- public void destroyObject(final PooledObject<PooledTestObject> obj,
final DestroyMode mode) throws Exception {
+ public void destroyObject(final PooledObject<PooledTestObject> obj,
final DestroyMode destroyMode) throws Exception {
obj.getObject().setActive(false);
// while destroying instances, yield control to other threads
// helps simulate threading errors
@@ -191,7 +191,7 @@ public class TestAbandonedObjectPool {
if (destroyLatency != 0) {
Thread.sleep(destroyLatency);
}
- obj.getObject().destroy(mode);
+ obj.getObject().destroy(destroyMode);
}
@Override
diff --git
a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
index 39e898c..cd36876 100644
---
a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
+++
b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
@@ -20,6 +20,7 @@ package org.apache.commons.pool2.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
@@ -906,6 +907,18 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
}
@Test
+ public void testAppendStats() {
+ assertFalse(gkoPool.getMessageStatistics());
+ assertEquals("foo", (gkoPool.appendStats("foo")));
+ try (final GenericKeyedObjectPool<?, ?> pool = new
GenericKeyedObjectPool<>(new SimpleFactory<>())) {
+ pool.setMessagesStatistics(true);
+ assertNotEquals("foo", (pool.appendStats("foo")));
+ pool.setMessagesStatistics(false);
+ assertEquals("foo", (pool.appendStats("foo")));
+ }
+ }
+
+ @Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testBlockedKeyDoesNotBlockPool() throws Exception {
gkoPool.setBlockWhenExhausted(true);
@@ -1025,6 +1038,7 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
gkoPool.close();
}
+
/**
* Test to make sure that clearOldest does not destroy instances that
have been checked out.
*
@@ -1061,7 +1075,6 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
}
}
-
// POOL-259
@Test
public void testClientWaitStats() throws Exception {
@@ -1483,6 +1496,27 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testExceptionInValidationDuringEviction() throws Exception {
+ gkoPool.setMaxIdlePerKey(1);
+ gkoPool.setMinEvictableIdleTime(Duration.ZERO);
+ gkoPool.setTestWhileIdle(true);
+
+ final String obj = gkoPool.borrowObject("one");
+ gkoPool.returnObject("one", obj);
+
+ simpleFactory.setThrowExceptionOnValidate(true);
+ try {
+ gkoPool.evict();
+ fail("Expecting RuntimeException");
+ } catch (final RuntimeException e) {
+ // expected
+ }
+ assertEquals(0, gkoPool.getNumActive());
+ assertEquals(0, gkoPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testExceptionOnActivateDuringBorrow() throws Exception {
final String obj1 = gkoPool.borrowObject("one");
final String obj2 = gkoPool.borrowObject("one");
@@ -1514,6 +1548,7 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
assertEquals(0, gkoPool.getNumIdle());
}
+
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testExceptionOnDestroyDuringBorrow() throws Exception {
@@ -1550,7 +1585,6 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
assertEquals(0, gkoPool.getNumIdle());
}
-
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testExceptionOnPassivateDuringReturn() throws Exception {
@@ -1563,27 +1597,6 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionInValidationDuringEviction() throws Exception {
- gkoPool.setMaxIdlePerKey(1);
- gkoPool.setMinEvictableIdleTime(Duration.ZERO);
- gkoPool.setTestWhileIdle(true);
-
- final String obj = gkoPool.borrowObject("one");
- gkoPool.returnObject("one", obj);
-
- simpleFactory.setThrowExceptionOnValidate(true);
- try {
- gkoPool.evict();
- fail("Expecting RuntimeException");
- } catch (final RuntimeException e) {
- // expected
- }
- assertEquals(0, gkoPool.getNumActive());
- assertEquals(0, gkoPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testFIFO() throws Exception {
gkoPool.setLifo(false);
final String key = "key";
@@ -1600,6 +1613,11 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
assertEquals( "key4", gkoPool.borrowObject(key),"new-4");
}
+ @Test
+ public void testGetStatsString() {
+ assertNotNull((gkoPool.getStatsString()));
+ }
+
/**
* Verify that threads waiting on a depleted pool get served when a
checked out object is
* invalidated.
diff --git
a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
index 1014588..fe646a3 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
@@ -20,6 +20,7 @@ package org.apache.commons.pool2.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -58,6 +59,7 @@ import org.apache.commons.pool2.VisitTracker;
import org.apache.commons.pool2.VisitTrackerFactory;
import org.apache.commons.pool2.Waiter;
import org.apache.commons.pool2.WaiterFactory;
+import org.apache.commons.pool2.impl.TestGenericKeyedObjectPool.SimpleFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -83,7 +85,9 @@ public class TestGenericObjectPool extends TestBaseObjectPool
{
} else {
genericObjectPool.evict();
}
- } catch (final Exception e) { /* Ignore */}
+ } catch (final Exception e) {
+ // Ignore.
+ }
}
}
@@ -999,6 +1003,18 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
assertEquals( 0, genericObjectPool.getNumActive(),"should be zero
active");
}
+ @Test
+ public void testAppendStats() {
+ assertFalse(genericObjectPool.getMessageStatistics());
+ assertEquals("foo", (genericObjectPool.appendStats("foo")));
+ try (final GenericObjectPool<?> pool = new GenericObjectPool<>(new
SimpleFactory())) {
+ pool.setMessagesStatistics(true);
+ assertNotEquals("foo", (pool.appendStats("foo")));
+ pool.setMessagesStatistics(false);
+ assertEquals("foo", (pool.appendStats("foo")));
+ }
+ }
+
/*
* Note: This test relies on timing for correct execution. There *should*
be
* enough margin for this to work correctly on most (all?) systems but be
@@ -1185,6 +1201,7 @@ public class TestGenericObjectPool extends
TestBaseObjectPool {
}
}
+
/**
* POOL-231 - verify that concurrent invalidates of the same object do not
* corrupt pool destroyCount.
@@ -1238,7 +1255,6 @@ public class TestGenericObjectPool extends
TestBaseObjectPool {
assertEquals(nIterations, genericObjectPool.getDestroyedCount());
}
-
@Test
public void testConstructorNullFactory() {
// add dummy assert (won't be invoked because of IAE) to avoid
"unused" warning
@@ -1916,6 +1932,14 @@ public class TestGenericObjectPool extends
TestBaseObjectPool {
}
}
+ @Test
+ public void testGetStatsString() {
+ try (final GenericObjectPool<String> pool = new GenericObjectPool<>(
+ new
TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
+ assertNotNull(pool.getStatsString());
+ }
+ }
+
/**
* Verify that threads waiting on a depleted pool get served when a
checked out object is
* invalidated.