Fix POOL-351
Add a configurable delay (default 10 seconds) to wait when shutting down an 
Evictor to allow the associated thread time to complete and current evictions 
and to terminate.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/pool/trunk@1767782 
13f79535-47bb-0310-9956-ffa450edef68


Project: http://git-wip-us.apache.org/repos/asf/commons-pool/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-pool/commit/4a20cdca
Tree: http://git-wip-us.apache.org/repos/asf/commons-pool/tree/4a20cdca
Diff: http://git-wip-us.apache.org/repos/asf/commons-pool/diff/4a20cdca

Branch: refs/heads/master
Commit: 4a20cdca923bd342360f821d7020538e985d9ec2
Parents: b15bc63
Author: Mark Thomas <ma...@apache.org>
Authored: Wed Nov 2 20:53:11 2016 +0000
Committer: Mark Thomas <ma...@apache.org>
Committed: Wed Nov 2 20:53:11 2016 +0000

----------------------------------------------------------------------
 src/changes/changes.xml                         |   5 +
 .../pool2/impl/BaseGenericObjectPool.java       |  30 +++-
 .../pool2/impl/BaseObjectPoolConfig.java        |  48 ++++++-
 .../commons/pool2/impl/EvictionTimer.java       | 144 +++++++------------
 .../pool2/impl/GenericKeyedObjectPool.java      |   1 +
 .../commons/pool2/impl/GenericObjectPool.java   |   1 +
 .../pool2/impl/TestGenericObjectPool.java       |   1 +
 7 files changed, 132 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 5cca806..dca51b3 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -71,6 +71,11 @@ The <action> type attribute can be add,update,fix,remove.
       Ensure that any class name used for evictionPolicyClassName represents a
       class that implements EvictionPolicy.
     </action>
+    <action dev="markt" issue="POOL-351" type="fix">
+      Add a configurable delay (default 10 seconds) to wait when shutting down
+      an Evictor to allow the associated thread time to complete and current
+      evictions and to terminate.
+    </action>
   </release>
   <release version="2.4.2" date="2015-08-01" description=
  "This is a patch release, including bug fixes only.">

http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
----------------------------------------------------------------------
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 8afa8f1..1f46811 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
 import java.util.Deque;
 import java.util.Iterator;
 import java.util.TimerTask;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
 import javax.management.InstanceAlreadyExistsException;
@@ -87,6 +88,8 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject {
     private volatile long softMinEvictableIdleTimeMillis =
             BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
     private volatile EvictionPolicy<T> evictionPolicy;
+    private long evictorShutdownTimeoutMillis =
+            BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS;
 
 
     // Internal (primarily state) attributes
@@ -632,6 +635,31 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject {
         }
     }
 
+    /**
+     * Gets the timeout that will be used when waiting for the Evictor to
+     * shutdown if this pool is closed and it is the only pool still using the
+     * the value for the Evictor.
+     *
+     * @return  The timeout in milliseconds that will be used while waiting for
+     *          the Evictor to shut down.
+     */
+    public long getEvictorShutdownTimeoutMillis() {
+        return evictorShutdownTimeoutMillis;
+    }
+
+    /**
+     * Sets the timeout that will be used when waiting for the Evictor to
+     * shutdown if this pool is closed and it is the only pool still using the
+     * the value for the Evictor.
+     *
+     * @param evictorShutdownTimeoutMillis  the timeout in milliseconds that
+     *                                      will be used while waiting for the
+     *                                      Evictor to shut down.
+     */
+    public void setEvictorShutdownTimeoutMillis(
+            final long evictorShutdownTimeoutMillis) {
+        this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis;
+    }
 
     /**
      * Closes the pool, destroys the remaining idle objects and, if registered
@@ -692,7 +720,7 @@ public abstract class BaseGenericObjectPool<T> extends 
BaseObject {
     final void startEvictor(final long delay) {
         synchronized (evictionLock) {
             if (null != evictor) {
-                EvictionTimer.cancel(evictor);
+                EvictionTimer.cancel(evictor, 10, TimeUnit.SECONDS);
                 evictor = null;
                 evictionIterator = null;
             }

http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java 
b/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
index 2f1a595..d635227 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseObjectPoolConfig.java
@@ -70,6 +70,15 @@ public abstract class BaseObjectPoolConfig extends 
BaseObject implements Cloneab
     public static final long DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1;
 
     /**
+     * The default value for {@code evictorShutdownTimeoutMillis} configuration
+     * attribute.
+     * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
+     * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     */
+    public static final long DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS =
+            10L * 1000L;
+
+    /**
      * The default value for the {@code numTestsPerEvictionRun} configuration
      * attribute.
      * @see GenericObjectPool#getNumTestsPerEvictionRun()
@@ -163,13 +172,16 @@ public abstract class BaseObjectPoolConfig extends 
BaseObject implements Cloneab
     private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS;
 
     private long minEvictableIdleTimeMillis =
-        DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+            DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
+
+    private long evictorShutdownTimeoutMillis =
+            DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS;
 
     private long softMinEvictableIdleTimeMillis =
             DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
 
     private int numTestsPerEvictionRun =
-        DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
+            DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
 
     private String evictionPolicyClassName = 
DEFAULT_EVICTION_POLICY_CLASS_NAME;
 
@@ -182,7 +194,7 @@ public abstract class BaseObjectPoolConfig extends 
BaseObject implements Cloneab
     private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE;
 
     private long timeBetweenEvictionRunsMillis =
-        DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
+            DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
 
     private boolean blockWhenExhausted = DEFAULT_BLOCK_WHEN_EXHAUSTED;
 
@@ -367,6 +379,36 @@ public abstract class BaseObjectPoolConfig extends 
BaseObject implements Cloneab
     }
 
     /**
+     * Get the value for the {@code evictorShutdownTimeoutMillis} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @return  The current setting of {@code evictorShutdownTimeoutMillis} for
+     *          this configuration instance
+     *
+     * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
+     * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     */
+    public long getEvictorShutdownTimeoutMillis() {
+        return evictorShutdownTimeoutMillis;
+    }
+
+    /**
+     * Set the value for the {@code evictorShutdownTimeoutMillis} configuration
+     * attribute for pools created with this configuration instance.
+     *
+     * @param evictorShutdownTimeoutMillis The new setting of
+     *        {@code evictorShutdownTimeoutMillis} for this configuration
+     *        instance
+     *
+     * @see GenericObjectPool#getEvictorShutdownTimeoutMillis()
+     * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutMillis()
+     */
+    public void setEvictorShutdownTimeoutMillis(
+            final long evictorShutdownTimeoutMillis) {
+        this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis;
+    }
+
+    /**
      * Get the value for the {@code testOnCreate} configuration attribute for
      * pools created with this configuration instance.
      *

http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java 
b/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java
index 191dc86..b448141 100644
--- a/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java
+++ b/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java
@@ -18,16 +18,19 @@ package org.apache.commons.pool2.impl;
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 
 /**
- * Provides a shared idle object eviction timer for all pools. This class wraps
- * the standard {@link Timer} and keeps track of how many pools are using it.
- * If no pools are using the timer, it is canceled. This prevents a thread
- * being left running which, in application server environments, can lead to
- * memory leads and/or prevent applications from shutting down or reloading
- * cleanly.
+ * Provides a shared idle object eviction timer for all pools. This class is
+ * currently implemented using {@link ScheduledThreadPoolExecutor}. This
+ * implementation may change in any future release. This class keeps track of
+ * how many pools are using it. If no pools are using the timer, it is 
canceled.
+ * This prevents a thread being left running which, in application server
+ * environments, can lead to memory leads and/or prevent applications from
+ * shutting down or reloading cleanly.
  * <p>
  * This class has package scope to prevent its inclusion in the pool public 
API.
  * The class declaration below should *not* be changed to public.
@@ -38,11 +41,11 @@ import java.util.TimerTask;
  */
 class EvictionTimer {
 
-    /** Timer instance */
-    private static Timer _timer; //@GuardedBy("EvictionTimer.class")
+    /** Executor instance */
+    private static ScheduledThreadPoolExecutor executor; 
//@GuardedBy("EvictionTimer.class")
 
     /** Static usage count tracker */
-    private static int _usageCount; //@GuardedBy("EvictionTimer.class")
+    private static int usageCount; //@GuardedBy("EvictionTimer.class")
 
     /** Prevent instantiation */
     private EvictionTimer() {
@@ -70,102 +73,55 @@ class EvictionTimer {
      * @param delay     Delay in milliseconds before task is executed
      * @param period    Time in milliseconds between executions
      */
-    static synchronized void schedule(final TimerTask task, final long delay, 
final long period) {
-        if (null == _timer) {
-            // Force the new Timer thread to be created with a context class
-            // loader set to the class loader that loaded this library
-            final ClassLoader ccl = AccessController.doPrivileged(
-                    new PrivilegedGetTccl());
-            try {
-                AccessController.doPrivileged(new PrivilegedSetTccl(
-                        EvictionTimer.class.getClassLoader()));
-                _timer = AccessController.doPrivileged(new 
PrivilegedNewEvictionTimer());
-            } finally {
-                AccessController.doPrivileged(new PrivilegedSetTccl(ccl));
-            }
+    static synchronized void schedule(final Runnable task, final long delay, 
final long period) {
+        if (null == executor) {
+            executor = new ScheduledThreadPoolExecutor(1, new 
EvictorThreadFactory());
         }
-        _usageCount++;
-        _timer.schedule(task, delay, period);
+        usageCount++;
+        executor.scheduleWithFixedDelay(task, delay, period, 
TimeUnit.MILLISECONDS);
     }
 
     /**
      * Remove the specified eviction task from the timer.
-     * @param task      Task to be scheduled
+     *
+     * @param task      Task to be cancelled
+     * @param timeout   If the associated executor is no longer required, how
+     *                  long should this thread wait for the executor to
+     *                  terminate?
+     * @param unit      The units for the specified timeout
      */
-    static synchronized void cancel(final TimerTask task) {
+    static synchronized void cancel(final TimerTask task, long timeout, 
TimeUnit unit) {
         task.cancel();
-        _usageCount--;
-        if (_usageCount == 0) {
-            _timer.cancel();
-            _timer = null;
-        }
-    }
-
-    /**
-     * {@link PrivilegedAction} used to get the ContextClassLoader
-     */
-    private static class PrivilegedGetTccl implements 
PrivilegedAction<ClassLoader> {
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public ClassLoader run() {
-            return Thread.currentThread().getContextClassLoader();
-        }
-    }
-
-    /**
-     * {@link PrivilegedAction} used to set the ContextClassLoader
-     */
-    private static class PrivilegedSetTccl implements PrivilegedAction<Void> {
-
-        /** ClassLoader */
-        private final ClassLoader classLoader;
-
-        /**
-         * Create a new PrivilegedSetTccl using the given classloader
-         * @param classLoader ClassLoader to use
-         */
-        PrivilegedSetTccl(final ClassLoader cl) {
-            this.classLoader = cl;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public Void run() {
-            Thread.currentThread().setContextClassLoader(classLoader);
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            final StringBuilder builder = new StringBuilder();
-            builder.append("PrivilegedSetTccl [classLoader=");
-            builder.append(classLoader);
-            builder.append("]");
-            return builder.toString();
+        usageCount--;
+        if (usageCount == 0) {
+            executor.shutdown();
+            try {
+                executor.awaitTermination(timeout, unit);
+            } catch (InterruptedException e) {
+                // Swallow
+                // Significant API changes would be required to propagate this
+            }
+            executor.setCorePoolSize(0);
+            executor = null;
         }
     }
 
-    /**
-     * {@link PrivilegedAction} used to create a new Timer. Creating the timer
-     * with a privileged action means the associated Thread does not inherit 
the
-     * current access control context. In a container environment, inheriting
-     * the current access control context is likely to result in retaining a
-     * reference to the thread context class loader which would be a memory
-     * leak.
-     */
-    private static class PrivilegedNewEvictionTimer implements 
PrivilegedAction<Timer> {
+    private static class EvictorThreadFactory implements ThreadFactory {
 
-        /**
-         * {@inheritDoc}
-         */
         @Override
-        public Timer run() {
-            return new Timer("commons-pool-EvictionTimer", true);
+        public Thread newThread(final Runnable r) {
+            final Thread t = new Thread(null, r, 
"commons-pool-evictor-thrreads");
+            t.setName("commons-pool-evictor");
+
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                @Override
+                public Void run() {
+                    
t.setContextClassLoader(EvictorThreadFactory.class.getClassLoader());
+                    return null;
+                }
+            });
+
+            return t;
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
----------------------------------------------------------------------
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 6976f2a..7fa21b5 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -255,6 +255,7 @@ public class GenericKeyedObjectPool<K,T> extends 
BaseGenericObjectPool<T>
         setTimeBetweenEvictionRunsMillis(
                 conf.getTimeBetweenEvictionRunsMillis());
         setEvictionPolicyClassName(conf.getEvictionPolicyClassName());
+        
setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis());
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
----------------------------------------------------------------------
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 487eec2..be0f508 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -318,6 +318,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         setSoftMinEvictableIdleTimeMillis(
                 conf.getSoftMinEvictableIdleTimeMillis());
         setEvictionPolicyClassName(conf.getEvictionPolicyClassName());
+        
setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis());
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/commons-pool/blob/4a20cdca/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
----------------------------------------------------------------------
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 c9014ac..838aab4 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
@@ -1705,6 +1705,7 @@ public class TestGenericObjectPool extends 
TestBaseObjectPool {
         
assertEquals("maxWait",expected.getMaxWaitMillis(),actual.getMaxWaitMillis());
         
assertEquals("minEvictableIdleTimeMillis",expected.getMinEvictableIdleTimeMillis(),actual.getMinEvictableIdleTimeMillis());
         
assertEquals("numTestsPerEvictionRun",expected.getNumTestsPerEvictionRun(),actual.getNumTestsPerEvictionRun());
+        
assertEquals("evictorShutdownTimeoutMillis",expected.getEvictorShutdownTimeoutMillis(),actual.getEvictorShutdownTimeoutMillis());
         
assertEquals("timeBetweenEvictionRunsMillis",expected.getTimeBetweenEvictionRunsMillis(),actual.getTimeBetweenEvictionRunsMillis());
     }
 

Reply via email to