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.

Reply via email to