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 06ba7ce  Add DestroyMode to provide context to destroyObject 
activations. Fixes POOL-387, needed by DBCP-567. (#43)
06ba7ce is described below

commit 06ba7ceb61f4a238307010bacc98c10241a1a243
Author: Phil Steitz <[email protected]>
AuthorDate: Mon Sep 14 07:07:14 2020 -0700

    Add DestroyMode to provide context to destroyObject activations. Fixes 
POOL-387, needed by DBCP-567. (#43)
---
 .../java/org/apache/commons/pool2/DestroyMode.java | 31 +++++++++++++
 .../org/apache/commons/pool2/KeyedObjectPool.java  | 27 +++++++++++
 .../commons/pool2/KeyedPooledObjectFactory.java    | 19 ++++++++
 .../java/org/apache/commons/pool2/ObjectPool.java  | 24 ++++++++++
 .../apache/commons/pool2/PooledObjectFactory.java  | 22 ++++++++-
 .../commons/pool2/impl/GenericKeyedObjectPool.java | 53 ++++++++++++++++------
 .../commons/pool2/impl/GenericObjectPool.java      | 48 ++++++++++++++------
 .../pool2/TestBasePoolableObjectFactory.java       | 37 +++++++++++++--
 .../pool2/impl/TestAbandonedObjectPool.java        | 45 +++++++++++++++++-
 9 files changed, 268 insertions(+), 38 deletions(-)

diff --git a/src/main/java/org/apache/commons/pool2/DestroyMode.java 
b/src/main/java/org/apache/commons/pool2/DestroyMode.java
new file mode 100644
index 0000000..09b1594
--- /dev/null
+++ b/src/main/java/org/apache/commons/pool2/DestroyMode.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool2;
+
+/**
+ * Destroy context provided to object factories via destroyObject methods.
+ * Values provide information about why the pool is asking for a pooled object
+ * to be destroyed.
+ *
+ * @since 2.8.2
+ */
+public enum DestroyMode {
+                         /** Normal destroy */
+                         NORMAL,
+                         /** Destroy abandoned object */
+                         ABANDONED
+}
diff --git a/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java 
b/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
index 9cca6e2..e47588a 100644
--- a/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/KeyedObjectPool.java
@@ -270,6 +270,33 @@ public interface KeyedObjectPool<K, V> extends Closeable {
      * @throws Exception if the instance cannot be invalidated
      */
     void invalidateObject(K key, V obj) throws Exception;
+    
+    
+    /**
+     * Invalidates an object from the pool, using the provided
+     * {@link DestroyMode}.
+     * <p>
+     * By contract, {@code obj} <strong>must</strong> have been obtained
+     * using {@link #borrowObject borrowObject} or a related method as defined
+     * in an implementation or sub-interface using a {@code key} that is
+     * equivalent to the one used to borrow the {@code Object} in the first
+     * place.
+     * </p>
+     * <p>
+     * This method should be used when an object that has been borrowed is
+     * determined (due to an exception or other problem) to be invalid.
+     * </p>
+     *
+     * @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
+     *
+     * @throws Exception if the instance cannot be invalidated
+     */
+    default void invalidateObject(K key, V obj, DestroyMode mode)
+                    throws Exception {
+                    invalidateObject(key, obj);
+    }
 
     /**
      * Return an instance to the pool. By contract, {@code obj}
diff --git 
a/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java 
b/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java
index bb753cf..37f14ad 100644
--- a/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java
+++ b/src/main/java/org/apache/commons/pool2/KeyedPooledObjectFactory.java
@@ -112,6 +112,25 @@ public interface KeyedPooledObjectFactory<K, V> {
      * @see KeyedObjectPool#invalidateObject
      */
     void destroyObject(K key, PooledObject<V> p) throws Exception;
+    
+    /**
+     * Destroy an instance no longer needed by the pool, using the provided 
{@link DestroyMode}.
+     *
+     * @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
+     *
+     * @throws Exception should be avoided as it may be swallowed by
+     *    the pool implementation.
+     *
+     * @see #validateObject
+     * @see KeyedObjectPool#invalidateObject
+     * @see #destroyObject(Object, PooledObject)
+     * @see DestroyMode
+     */
+    default void destroyObject(K key, PooledObject<V> p, DestroyMode mode) 
throws Exception {
+        destroyObject(key, p);
+    }
 
     /**
      * Ensures that the instance is safe to be returned by the pool.
diff --git a/src/main/java/org/apache/commons/pool2/ObjectPool.java 
b/src/main/java/org/apache/commons/pool2/ObjectPool.java
index ca998fc..dcc384d 100644
--- a/src/main/java/org/apache/commons/pool2/ObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/ObjectPool.java
@@ -183,6 +183,29 @@ public interface ObjectPool<T> extends Closeable {
      * @throws Exception if the instance cannot be invalidated
      */
     void invalidateObject(T obj) throws Exception;
+    
+    /**
+     * Invalidates an object from the pool, using the provided
+     * {@link DestroyMode}
+     * <p>
+     * By contract, {@code obj} <strong>must</strong> have been obtained
+     * using {@link #borrowObject} or a related method as defined in an
+     * implementation or sub-interface.
+     * </p>
+     * <p>
+     * This method should be used when an object that has been borrowed is
+     * determined (due to an exception or other problem) to be invalid.
+     * </p>
+     *
+     * @param obj a {@link #borrowObject borrowed} instance to be disposed.
+     * @param mode destroy activation context provided to the factory
+     *
+     * @throws Exception if the instance cannot be invalidated
+     */
+    default void invalidateObject(T obj, DestroyMode mode)
+                    throws Exception {
+        invalidateObject(obj);
+    }
 
     /**
      * Returns an instance to the pool. By contract, {@code obj}
@@ -201,4 +224,5 @@ public interface ObjectPool<T> extends Closeable {
      * @throws Exception if an instance cannot be returned to the pool
      */
     void returnObject(T obj) throws Exception;
+
 }
diff --git a/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java 
b/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java
index cc7f794..d16e3c1 100644
--- a/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java
+++ b/src/main/java/org/apache/commons/pool2/PooledObjectFactory.java
@@ -84,7 +84,8 @@ public interface PooledObjectFactory<T> {
   PooledObject<T> makeObject() throws Exception;
 
   /**
-   * Destroys an instance no longer needed by the pool.
+   * Destroys an instance no longer needed by the pool, using the default 
(NORMAL)
+   * DestroyMode.
    * <p>
    * It is important for implementations of this method to be aware that there
    * is no guarantee about what state {@code obj} will be in and the
@@ -104,6 +105,25 @@ public interface PooledObjectFactory<T> {
    * @see ObjectPool#invalidateObject
    */
   void destroyObject(PooledObject<T> p) throws Exception;
+  
+  /**
+   * Destroys an instance no longer needed by the pool, using the provided
+   * DestroyMode.
+   *
+   * @param p a {@code PooledObject} wrapping the instance to be destroyed
+   * @param mode DestroyMode providing context to the factory
+   *
+   * @throws Exception should be avoided as it may be swallowed by
+   *    the pool implementation.
+   *
+   * @see #validateObject
+   * @see ObjectPool#invalidateObject
+   * @see #destroyObject(PooledObject)
+   * @see DestroyMode
+   */
+  default void destroyObject(PooledObject<T> p, DestroyMode mode) throws 
Exception {
+      destroyObject(p);
+  }
 
   /**
    * Ensures that the instance is safe to be returned by the pool.
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 310c4e7..ef661eb 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -33,6 +33,7 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.apache.commons.pool2.DestroyMode;
 import org.apache.commons.pool2.KeyedObjectPool;
 import org.apache.commons.pool2.KeyedPooledObjectFactory;
 import org.apache.commons.pool2.PoolUtils;
@@ -376,7 +377,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                         factory.activateObject(key, p);
                     } catch (final Exception e) {
                         try {
-                            destroy(key, p, true);
+                            destroy(key, p, true, DestroyMode.NORMAL);
                         } catch (final Exception e1) {
                             // Ignore - activation failure is more important
                         }
@@ -399,7 +400,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                         }
                         if (!validate) {
                             try {
-                                destroy(key, p, true);
+                                destroy(key, p, true, DestroyMode.NORMAL);
                                 
destroyedByBorrowValidationCount.incrementAndGet();
                             } catch (final Exception e) {
                                 // Ignore - validation failure is more 
important
@@ -471,7 +472,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         try {
             if (getTestOnReturn() && !factory.validateObject(key, p)) {
                 try {
-                    destroy(key, p, true);
+                    destroy(key, p, true, DestroyMode.NORMAL);
                 } catch (final Exception e) {
                     swallowException(e);
                 }
@@ -484,7 +485,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             } catch (final Exception e1) {
                 swallowException(e1);
                 try {
-                    destroy(key, p, true);
+                    destroy(key, p, true, DestroyMode.NORMAL);
                 } catch (final Exception e) {
                     swallowException(e);
                 }
@@ -503,7 +504,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
 
             if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) {
                 try {
-                    destroy(key, p, true);
+                    destroy(key, p, true, DestroyMode.NORMAL);
                 } catch (final Exception e) {
                     swallowException(e);
                 }
@@ -542,7 +543,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             }
         }
     }
-
+    
     /**
      * {@inheritDoc}
      * <p>
@@ -559,6 +560,26 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
      */
     @Override
     public void invalidateObject(final K key, final T obj) throws Exception {
+        invalidateObject(key, obj, DestroyMode.NORMAL);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Activation of this method decrements the active count associated with
+     * the given keyed pool and attempts to destroy {@code obj.}
+     *
+     * @param key pool key
+     * @param obj instance to invalidate
+     * @param mode DestroyMode context provided to factory
+     *
+     * @throws Exception             if an exception occurs destroying the
+     *                               object
+     * @throws IllegalStateException if obj does not belong to the pool
+     *                               under the given key
+     */
+    @Override
+    public void invalidateObject(final K key, final T obj, DestroyMode mode) 
throws Exception {
 
         final ObjectDeque<T> objectDeque = poolMap.get(key);
 
@@ -569,7 +590,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
         }
         synchronized (p) {
             if (p.getState() != PooledObjectState.INVALID) {
-                destroy(key, p, true);
+                destroy(key, p, true, mode);
             }
         }
         if (objectDeque.idleObjects.hasTakeWaiters()) {
@@ -627,7 +648,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
 
             while (p != null) {
                 try {
-                    destroy(key, p, true);
+                    destroy(key, p, true, DestroyMode.NORMAL);
                 } catch (final Exception e) {
                     swallowException(e);
                 }
@@ -760,7 +781,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
             // Assume the destruction succeeds
             boolean destroyed = true;
             try {
-                destroyed = destroy(key, p, false);
+                destroyed = destroy(key, p, false, DestroyMode.NORMAL);
             } catch (final Exception e) {
                 swallowException(e);
             }
@@ -937,7 +958,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                 }
 
                 if (evict) {
-                    destroy(evictionKey, underTest, true);
+                    destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
                     destroyedByEvictorCount.incrementAndGet();
                 } else {
                     if (testWhileIdle) {
@@ -946,18 +967,18 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                             factory.activateObject(evictionKey, underTest);
                             active = true;
                         } catch (final Exception e) {
-                            destroy(evictionKey, underTest, true);
+                            destroy(evictionKey, underTest, true, 
DestroyMode.NORMAL);
                             destroyedByEvictorCount.incrementAndGet();
                         }
                         if (active) {
                             if (!factory.validateObject(evictionKey, 
underTest)) {
-                                destroy(evictionKey, underTest, true);
+                                destroy(evictionKey, underTest, true, 
DestroyMode.NORMAL);
                                 destroyedByEvictorCount.incrementAndGet();
                             } else {
                                 try {
                                     factory.passivateObject(evictionKey, 
underTest);
                                 } catch (final Exception e) {
-                                    destroy(evictionKey, underTest, true);
+                                    destroy(evictionKey, underTest, true, 
DestroyMode.NORMAL);
                                     destroyedByEvictorCount.incrementAndGet();
                                 }
                             }
@@ -1077,10 +1098,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
+     *
      * @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)
+    private boolean destroy(final K key, final PooledObject<T> toDestroy, 
final boolean always, DestroyMode mode)
             throws Exception {
 
         final ObjectDeque<T> objectDeque = register(key);
@@ -1101,7 +1124,7 @@ public class GenericKeyedObjectPool<K, T> extends 
BaseGenericObjectPool<T>
                 toDestroy.invalidate();
 
                 try {
-                    factory.destroyObject(key, toDestroy);
+                    factory.destroyObject(key, toDestroy, mode);
                 } finally {
                     objectDeque.getCreateCount().decrementAndGet();
                     destroyedCount.incrementAndGet();
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 09c7c5c..e791ad7 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -16,6 +16,7 @@
  */
 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;
@@ -458,7 +459,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                     factory.activateObject(p);
                 } catch (final Exception e) {
                     try {
-                        destroy(p);
+                        destroy(p, DestroyMode.NORMAL);
                     } catch (final Exception e1) {
                         // Ignore - activation failure is more important
                     }
@@ -481,7 +482,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                     }
                     if (!validate) {
                         try {
-                            destroy(p);
+                            destroy(p, DestroyMode.NORMAL);
                             destroyedByBorrowValidationCount.incrementAndGet();
                         } catch (final Exception e) {
                             // Ignore - validation failure is more important
@@ -538,7 +539,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
 
         if (getTestOnReturn() && !factory.validateObject(p)) {
             try {
-                destroy(p);
+                destroy(p, DestroyMode.NORMAL);
             } catch (final Exception e) {
                 swallowException(e);
             }
@@ -556,7 +557,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         } catch (final Exception e1) {
             swallowException(e1);
             try {
-                destroy(p);
+                destroy(p, DestroyMode.NORMAL);
             } catch (final Exception e) {
                 swallowException(e);
             }
@@ -577,7 +578,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         final int maxIdleSave = getMaxIdle();
         if (isClosed() || maxIdleSave > -1 && maxIdleSave <= 
idleObjects.size()) {
             try {
-                destroy(p);
+                destroy(p, DestroyMode.NORMAL);
             } catch (final Exception e) {
                 swallowException(e);
             }
@@ -606,7 +607,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * {@inheritDoc}
      * <p>
      * Activation of this method decrements the active count and attempts to
-     * destroy the instance.
+     * destroy the instance, using the default (NORMAL) {@link DestroyMode}.
      * </p>
      *
      * @throws Exception             if an exception occurs destroying the
@@ -615,6 +616,22 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      */
     @Override
     public void invalidateObject(final T obj) throws Exception {
+        invalidateObject(obj, DestroyMode.NORMAL);
+    }
+    
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Activation of this method decrements the active count and attempts to
+     * destroy the instance, using the provided {@link DestroyMode}.
+     * </p>
+     *
+     * @throws Exception             if an exception occurs destroying the
+     *                               object
+     * @throws IllegalStateException if obj does not belong to this pool
+     */
+    @Override
+    public void invalidateObject(final T obj, DestroyMode mode) throws 
Exception {
         final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
         if (p == null) {
             if (isAbandonedConfig()) {
@@ -625,7 +642,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         }
         synchronized (p) {
             if (p.getState() != PooledObjectState.INVALID) {
-                destroy(p);
+                destroy(p, mode);
             }
         }
         ensureIdle(1, false);
@@ -655,7 +672,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
 
         while (p != null) {
             try {
-                destroy(p);
+                destroy(p, DestroyMode.NORMAL);
             } catch (final Exception e) {
                 swallowException(e);
             }
@@ -775,7 +792,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                     }
 
                     if (evict) {
-                        destroy(underTest);
+                        destroy(underTest, DestroyMode.NORMAL);
                         destroyedByEvictorCount.incrementAndGet();
                     } else {
                         if (testWhileIdle) {
@@ -784,18 +801,18 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                                 factory.activateObject(underTest);
                                 active = true;
                             } catch (final Exception e) {
-                                destroy(underTest);
+                                destroy(underTest, DestroyMode.NORMAL);
                                 destroyedByEvictorCount.incrementAndGet();
                             }
                             if (active) {
                                 if (!factory.validateObject(underTest)) {
-                                    destroy(underTest);
+                                    destroy(underTest, DestroyMode.NORMAL);
                                     destroyedByEvictorCount.incrementAndGet();
                                 } else {
                                     try {
                                         factory.passivateObject(underTest);
                                     } catch (final Exception e) {
-                                        destroy(underTest);
+                                        destroy(underTest, DestroyMode.NORMAL);
                                         
destroyedByEvictorCount.incrementAndGet();
                                     }
                                 }
@@ -926,16 +943,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
      *
      * @throws Exception If the factory fails to destroy the pooled object
      *                   cleanly
      */
-    private void destroy(final PooledObject<T> toDestroy) throws Exception {
+    private void destroy(final PooledObject<T> toDestroy, final DestroyMode 
mode) throws Exception {
         toDestroy.invalidate();
         idleObjects.remove(toDestroy);
         allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
         try {
-            factory.destroyObject(toDestroy);
+            factory.destroyObject(toDestroy, mode);
         } finally {
             destroyedCount.incrementAndGet();
             createCount.decrementAndGet();
@@ -1071,7 +1089,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                 pooledObject.printStackTrace(ac.getLogWriter());
             }
             try {
-                invalidateObject(pooledObject.getObject());
+                invalidateObject(pooledObject.getObject(), 
DestroyMode.ABANDONED);
             } catch (final Exception e) {
                 e.printStackTrace();
             }
diff --git 
a/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java 
b/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
index 11fa724..b19d934 100644
--- a/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
+++ b/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
@@ -16,8 +16,11 @@
  */
 package org.apache.commons.pool2;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.commons.pool2.impl.DefaultPooledObject;
 import org.junit.Test;
 
@@ -27,21 +30,45 @@ public class TestBasePoolableObjectFactory {
 
     @Test
     public void testDefaultMethods() throws Exception {
-        final PooledObjectFactory<Object> factory = new TestFactory();
+        final PooledObjectFactory<AtomicInteger> factory = new TestFactory();
 
         factory.activateObject(null); // a no-op
         factory.passivateObject(null); // a no-op
         factory.destroyObject(null); // a no-op
         assertTrue(factory.validateObject(null)); // constant true
     }
+    
+    /**
+     * Default destroy does nothing to underlying AtomicInt, ABANDONED mode
+     * increments the value.  Verify that destroy with no mode does default,
+     * destroy with ABANDONED mode increments.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testDestroyModes() throws Exception {
+        final PooledObjectFactory<AtomicInteger> factory = new TestFactory();
+        final PooledObject<AtomicInteger> pooledObj = factory.makeObject();
+        final AtomicInteger obj = pooledObj.getObject();
+        factory.destroyObject(pooledObj);
+        assertEquals(0, obj.get());
+        factory.destroyObject(pooledObj, DestroyMode.ABANDONED);
+        assertEquals(1, obj.get());    
+    }
 
-    private static class TestFactory extends BasePooledObjectFactory<Object> {
+    private static class TestFactory extends 
BasePooledObjectFactory<AtomicInteger> {
         @Override
-        public Object create() throws Exception {
-            return null;
+        public AtomicInteger create() throws Exception {
+            return new AtomicInteger(0);
         }
+        @Override 
+        public void destroyObject(PooledObject<AtomicInteger> p, DestroyMode 
mode){
+            if (mode.equals(DestroyMode.ABANDONED)) {
+                p.getObject().incrementAndGet();
+            }
+        } 
         @Override
-        public PooledObject<Object> wrap(final Object value) {
+        public PooledObject<AtomicInteger> wrap(final AtomicInteger value) {
             return new DefaultPooledObject<>(value);
         }
     }
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 0c9ebfd..6f0634b 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
@@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
 
+import org.apache.commons.pool2.DestroyMode;
 import org.apache.commons.pool2.PooledObject;
 import org.apache.commons.pool2.PooledObjectFactory;
 import org.apache.commons.pool2.TrackedUse;
@@ -199,6 +200,33 @@ public class TestAbandonedObjectPool {
         Assert.assertEquals(0, pool.getNumActive());
         Assert.assertEquals(5, pool.getDestroyedCount());
     }
+    
+    public void testDestroyModeAbandoned() throws Exception {
+        abandonedConfig = new AbandonedConfig();
+        abandonedConfig.setRemoveAbandonedOnMaintenance(true);
+        abandonedConfig.setRemoveAbandonedTimeout(1);
+        pool.close();  // Unregister pool created by setup
+        pool = new GenericObjectPool<>(
+             // validate takes 1 second
+             new SimpleFactory(0, 0),
+             new GenericObjectPoolConfig<PooledTestObject>(), abandonedConfig);
+        pool.setTimeBetweenEvictionRunsMillis(50);
+        // Borrow an object, wait long enough for it to be abandoned
+        final PooledTestObject obj = pool.borrowObject();
+        Thread.sleep(100);
+        Assert.assertTrue(obj.isDetached());
+    }
+    
+    public void testDestroyModeNormal() throws Exception {
+        abandonedConfig = new AbandonedConfig();
+        pool.close();  // Unregister pool created by setup
+        pool = new GenericObjectPool<>(new SimpleFactory(0, 0));
+        pool.setMaxIdle(0);
+        final PooledTestObject obj = pool.borrowObject();
+        pool.returnObject(obj);
+        Assert.assertTrue(obj.isDestroyed());
+        Assert.assertFalse(obj.isDetached());
+    }
 
     /**
      * Verify that an object that the evictor identifies as abandoned while it
@@ -358,6 +386,11 @@ public class TestAbandonedObjectPool {
 
         @Override
         public void destroyObject(final PooledObject<PooledTestObject> obj) 
throws Exception {
+            destroyObject(obj, DestroyMode.NORMAL);
+        }
+        
+        @Override
+        public void destroyObject(final PooledObject<PooledTestObject> obj, 
DestroyMode mode) throws Exception {
             obj.getObject().setActive(false);
             // while destroying instances, yield control to other threads
             // helps simulate threading errors
@@ -365,7 +398,7 @@ public class TestAbandonedObjectPool {
             if (destroyLatency != 0) {
                 Thread.sleep(destroyLatency);
             }
-            obj.getObject().destroy();
+            obj.getObject().destroy(mode);
         }
     }
 }
@@ -376,6 +409,7 @@ class PooledTestObject implements TrackedUse {
     private int _hash = 0;
     private boolean _abandoned = false;
     private static final AtomicInteger hash = new AtomicInteger();
+    private boolean detached = false;  // destroy-abandoned "detaches"
 
     public PooledTestObject() {
         _hash = hash.incrementAndGet();
@@ -389,13 +423,20 @@ class PooledTestObject implements TrackedUse {
         return active;
     }
 
-    public void destroy() {
+    public void destroy(DestroyMode mode) {
         destroyed = true;
+        if (mode.equals(DestroyMode.ABANDONED)) {
+            detached = true;
+        }
     }
 
     public boolean isDestroyed() {
         return destroyed;
     }
+    
+    public boolean isDetached() {
+        return detached;
+    }
 
     @Override
     public int hashCode() {

Reply via email to