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

commit a6930dbc5d88e1001364b5638c74268ebd9bdc55
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Mon Feb 15 18:47:04 2021 -0500

    Add and use java.time.Duration APIs timeouts instead of using ints for
    seconds.
---
 .../org/apache/commons/pool2/PooledObject.java     |  29 +++++-
 .../commons/pool2/impl/BaseGenericObjectPool.java  |  25 +++--
 .../commons/pool2/impl/GenericKeyedObjectPool.java |   5 +-
 .../commons/pool2/impl/GenericObjectPool.java      | 102 +++++++++++++++------
 .../pool2/impl/TestBaseGenericObjectPool.java      |  18 ++--
 .../pool2/impl/TestDefaultPooledObject.java        |  25 +++--
 6 files changed, 142 insertions(+), 62 deletions(-)

diff --git a/src/main/java/org/apache/commons/pool2/PooledObject.java 
b/src/main/java/org/apache/commons/pool2/PooledObject.java
index 96f726d..2e46997 100644
--- a/src/main/java/org/apache/commons/pool2/PooledObject.java
+++ b/src/main/java/org/apache/commons/pool2/PooledObject.java
@@ -17,6 +17,7 @@
 package org.apache.commons.pool2;
 
 import java.io.PrintWriter;
+import java.time.Duration;
 import java.util.Deque;
 
 /**
@@ -75,7 +76,19 @@ public interface PooledObject<T> extends 
Comparable<PooledObject<T>> {
     boolean equals(Object obj);
 
     /**
-     * Gets the time in milliseconds that this object last spent in the
+     * Gets the amount of time this object last spent in the
+     * active state (it may still be active in which case subsequent calls will
+     * return an increased value).
+     *
+     * @return The duration last spent in the active state
+     * @since 2.12.0
+     */
+    default Duration getActiveTime() {
+        return Duration.ofMillis(getActiveTimeMillis());
+    }
+
+    /**
+     * Gets the amount of time in milliseconds this object last spent in the
      * active state (it may still be active in which case subsequent calls will
      * return an increased value).
      *
@@ -102,7 +115,19 @@ public interface PooledObject<T> extends 
Comparable<PooledObject<T>> {
     long getCreateTime();
 
     /**
-     * Gets the time in milliseconds that this object last spend in the
+     * Gets the amount of time that this object last spend in the
+     * idle state (it may still be idle in which case subsequent calls will
+     * return an increased value).
+     *
+     * @return The amount of time in last spent in the idle state.
+     * @since 2.10.0
+     */
+    default Duration getIdleTime() {
+        return Duration.ofMillis(getIdleTimeMillis());
+    }
+
+    /**
+     * Gets the amount of time in milliseconds that this object last spend in 
the
      * idle state (it may still be idle in which case subsequent calls will
      * return an increased value).
      *
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 a0d05c9..1ab282a 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
@@ -248,7 +248,7 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject {
          *
          * @param size number of values to maintain in the cache.
          */
-        public StatsStore(final int size) {
+        StatsStore(final int size) {
             this.size = size;
             values = new AtomicLong[size];
             for (int i = 0; i < size; i++) {
@@ -256,13 +256,17 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject {
             }
         }
 
+        void add(Duration value) {
+            add(value.toMillis());
+        }
+
         /**
          * Adds a value to the cache.  If the cache is full, one of the
          * existing values is replaced by the new value.
          *
          * @param value new value to add to the cache.
          */
-        public synchronized void add(final long value) {
+        synchronized void add(final long value) {
             values[index].set(value);
             index++;
             if (index == size) {
@@ -1544,30 +1548,33 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject {
 
     /**
      * Updates statistics after an object is borrowed from the pool.
+     *
      * @param p object borrowed from the pool
      * @param waitTime time (in milliseconds) that the borrowing thread had to 
wait
      */
-    final void updateStatsBorrow(final PooledObject<T> p, final long waitTime) 
{
+    final void updateStatsBorrow(final PooledObject<T> p, final Duration 
waitTime) {
         borrowedCount.incrementAndGet();
-        idleTimes.add(p.getIdleTimeMillis());
+        idleTimes.add(p.getIdleTime());
         waitTimes.add(waitTime);
+        final long waitTimeMillis = waitTime.toMillis();
 
         // lock-free optimistic-locking maximum
-        long currentMax;
+        long currentMaxMillis;
         do {
-            currentMax = maxBorrowWaitTimeMillis.get();
-            if (currentMax >= waitTime) {
+            currentMaxMillis = maxBorrowWaitTimeMillis.get();
+            if (currentMaxMillis >= waitTimeMillis) {
                 break;
             }
-        } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMax, waitTime));
+        } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMaxMillis, 
waitTimeMillis));
     }
 
     /**
      * Updates statistics after an object is returned to the pool.
+     *
      * @param activeTime the amount of time (in milliseconds) that the 
returning
      * object was checked out
      */
-    final void updateStatsReturn(final long activeTime) {
+    final void updateStatsReturn(final Duration activeTime) {
         returnedCount.incrementAndGet();
         activeTimes.add(activeTime);
     }
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 e4098c4..50c7f2b 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.pool2.impl;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.HashMap;
@@ -474,7 +475,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             deregister(key);
         }
 
-        updateStatsBorrow(p, System.currentTimeMillis() - waitTimeMillis);
+        updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - 
waitTimeMillis));
 
         return p.getObject();
     }
@@ -1427,7 +1428,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
 
         markReturningState(p);
 
-        final long activeTime = p.getActiveTimeMillis();
+        final Duration activeTime = p.getActiveTime();
 
         try {
             if (getTestOnReturn() && !factory.validateObject(key, p)) {
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 e602a5e..97d38a2 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -16,16 +16,6 @@
  */
 package org.apache.commons.pool2.impl;
 
-import org.apache.commons.pool2.DestroyMode;
-import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.PoolUtils;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.PooledObjectFactory;
-import org.apache.commons.pool2.PooledObjectState;
-import org.apache.commons.pool2.SwallowedExceptionListener;
-import org.apache.commons.pool2.TrackedUse;
-import org.apache.commons.pool2.UsageTracking;
-
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -34,9 +24,18 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.commons.pool2.DestroyMode;
+import org.apache.commons.pool2.ObjectPool;
+import org.apache.commons.pool2.PoolUtils;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.PooledObjectState;
+import org.apache.commons.pool2.SwallowedExceptionListener;
+import org.apache.commons.pool2.TrackedUse;
+import org.apache.commons.pool2.UsageTracking;
+
 /**
  * A configurable {@link ObjectPool} implementation.
  * <p>
@@ -272,7 +271,7 @@ 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 borrowMaxWait The time to wait for an object
      *                            to become available
      *
      * @return object instance from the pool
@@ -281,14 +280,14 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      *
      * @throws Exception if an object instance cannot be returned due to an
      *                   error
+     * @since 2.10.0
      */
-    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
+    public T borrowObject(final Duration borrowMaxWait) throws Exception {
         assertOpen();
 
         final AbandonedConfig ac = this.abandonedConfig;
-        if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
-                (getNumIdle() < 2) &&
-                (getNumActive() > getMaxTotal() - 3) ) {
+        if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 
2) &&
+                (getNumActive() > getMaxTotal() - 3)) {
             removeAbandoned(ac);
         }
 
@@ -312,16 +311,14 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
             }
             if (blockWhenExhausted) {
                 if (p == null) {
-                    if (borrowMaxWaitMillis < 0) {
+                    if (borrowMaxWait.isNegative()) {
                         p = idleObjects.takeFirst();
                     } else {
-                        p = idleObjects.pollFirst(borrowMaxWaitMillis,
-                                TimeUnit.MILLISECONDS);
+                        p = idleObjects.pollFirst(borrowMaxWait);
                     }
                 }
                 if (p == null) {
-                    throw new NoSuchElementException(
-                            "Timeout waiting for idle object");
+                    throw new NoSuchElementException("Timeout waiting for idle 
object");
                 }
             } else {
                 if (p == null) {
@@ -343,8 +340,7 @@ 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("Unable to activate object");
                         nsee.initCause(e);
                         throw nsee;
                     }
@@ -367,8 +363,7 @@ 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("Unable to validate object");
                             nsee.initCause(validationThrowable);
                             throw nsee;
                         }
@@ -377,12 +372,65 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
             }
         }
 
-        updateStatsBorrow(p, System.currentTimeMillis() - waitTimeMillis);
+        updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - 
waitTimeMillis));
 
         return p.getObject();
     }
 
     /**
+     * Borrows an object from the pool using the specific waiting time which 
only
+     * applies if {@link #getBlockWhenExhausted()} is true.
+     * <p>
+     * If there is one or more idle instance available in the pool, 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 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
+     * 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}
+     * is thrown.
+     * </p>
+     * <p>
+     * If the pool is exhausted (no available idle instances and no capacity to
+     * create new ones), this method will either block (if
+     * {@link #getBlockWhenExhausted()} is true) or throw a
+     * {@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.
+     * </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 borrowMaxWaitMillis 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
+     */
+    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
+        return borrowObject(Duration.ofMillis(borrowMaxWaitMillis));
+    }
+
+    /**
      * Clears any objects sitting idle in the pool by removing them from the
      * idle instance pool and then invoking the configured
      * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each
@@ -1080,7 +1128,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
 
         markReturningState(p);
 
-        final long activeTime = p.getActiveTimeMillis();
+        final Duration activeTime = p.getActiveTime();
 
         if (getTestOnReturn() && !factory.validateObject(p)) {
             try {
diff --git 
a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java 
b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
index 30307d2..6beb283 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
@@ -50,7 +50,7 @@ public class TestBaseGenericObjectPool {
     @Test
     public void testActiveTimeStatistics() {
         for (int i = 0; i < 99; i++) { // must be < 
MEAN_TIMING_STATS_CACHE_SIZE
-            pool.updateStatsReturn(i);
+            pool.updateStatsReturn(Duration.ofMillis(i));
         }
         assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE);
     }
@@ -58,10 +58,10 @@ public class TestBaseGenericObjectPool {
     @Test
     public void testBorrowWaitStatistics() {
         final DefaultPooledObject<String> p = (DefaultPooledObject<String>) 
factory.makeObject();
-        pool.updateStatsBorrow(p, 10);
-        pool.updateStatsBorrow(p, 20);
-        pool.updateStatsBorrow(p, 20);
-        pool.updateStatsBorrow(p, 30);
+        pool.updateStatsBorrow(p, Duration.ofMillis(10));
+        pool.updateStatsBorrow(p, Duration.ofMillis(20));
+        pool.updateStatsBorrow(p, Duration.ofMillis(20));
+        pool.updateStatsBorrow(p, Duration.ofMillis(30));
         assertEquals(20, pool.getMeanBorrowWaitTimeMillis(), Double.MIN_VALUE);
         assertEquals(30, pool.getMaxBorrowWaitTimeMillis(), 0);
     }
@@ -69,13 +69,13 @@ public class TestBaseGenericObjectPool {
     public void testBorrowWaitStatisticsMax() {
         final DefaultPooledObject<String> p = (DefaultPooledObject<String>) 
factory.makeObject();
         assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
-        pool.updateStatsBorrow(p, 0);
+        pool.updateStatsBorrow(p, Duration.ZERO);
         assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
-        pool.updateStatsBorrow(p, 20);
+        pool.updateStatsBorrow(p, Duration.ofMillis(20));
         assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
-        pool.updateStatsBorrow(p, 20);
+        pool.updateStatsBorrow(p, Duration.ofMillis(20));
         assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
-        pool.updateStatsBorrow(p, 10);
+        pool.updateStatsBorrow(p, Duration.ofMillis(10));
         assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
     }
 
diff --git 
a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java 
b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java
index cc3f63e..ad02d5c 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java
@@ -35,21 +35,20 @@ public class TestDefaultPooledObject {
      * @throws Exception May occur in some failure modes
      */
     @Test
-    public void testgetIdleTimeMillis() throws Exception {
+    public void testGetIdleTimeMillis() throws Exception {
         final DefaultPooledObject<Object> dpo = new DefaultPooledObject<>(new 
Object());
         final AtomicBoolean negativeIdleTimeReturned = new 
AtomicBoolean(false);
-        final ExecutorService executor = Executors.newFixedThreadPool(
-                                      
Runtime.getRuntime().availableProcessors()*3);
+        final ExecutorService executor = 
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 3);
         final Runnable allocateAndDeallocateTask = () -> {
-            for (int i1=0;i1<10000;i1++) {
-                if (dpo.getIdleTimeMillis() < 0) {
+            for (int i1 = 0; i1 < 10000; i1++) {
+                if (dpo.getIdleTime().isNegative()) {
                     negativeIdleTimeReturned.set(true);
                     break;
                 }
             }
             dpo.allocate();
-            for (int i2=0;i2<10000;i2++) {
-                if (dpo.getIdleTimeMillis() < 0) {
+            for (int i2 = 0; i2 < 10000; i2++) {
+                if (dpo.getIdleTime().isNegative()) {
                     negativeIdleTimeReturned.set(true);
                     break;
                 }
@@ -57,8 +56,8 @@ public class TestDefaultPooledObject {
             dpo.deallocate();
         };
         final Runnable getIdleTimeTask = () -> {
-            for (int i=0;i<10000;i++) {
-                if (dpo.getIdleTimeMillis() < 0) {
+            for (int i = 0; i < 10000; i++) {
+                if (dpo.getIdleTime().isNegative()) {
                     negativeIdleTimeReturned.set(true);
                     break;
                 }
@@ -67,15 +66,15 @@ public class TestDefaultPooledObject {
         final double probabilityOfAllocationTask = 0.7;
         final List<Future<?>> futures = new ArrayList<>();
         for (int i = 1; i <= 10000; i++) {
-            final Runnable randomTask = Math.random() < 
probabilityOfAllocationTask ?
-                                  allocateAndDeallocateTask : getIdleTimeTask;
+            final Runnable randomTask = Math.random() < 
probabilityOfAllocationTask ? allocateAndDeallocateTask
+                    : getIdleTimeTask;
             futures.add(executor.submit(randomTask));
         }
-        for (final Future<?> future: futures) {
+        for (final Future<?> future : futures) {
             future.get();
         }
         assertFalse(negativeIdleTimeReturned.get(),
-           "DefaultPooledObject.getIdleTimeMillis() returned a negative 
value");
+                "DefaultPooledObject.getIdleTimeMillis() returned a negative 
value");
     }
 
 }
\ No newline at end of file

Reply via email to