You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/07/03 00:09:10 UTC
[commons-pool] 02/04: Clean ups.
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 4163d8a6e611329546e7dd2ece2d46a46e090599
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Jul 2 19:18:51 2022 -0400
Clean ups.
- Remove end of line whitespace
- PMD UselessParentheses
- Use final
- Use diamonds
---
.../java/org/apache/commons/pool2/PoolUtils.java | 2 +-
.../commons/pool2/impl/DefaultEvictionPolicy.java | 108 +-
.../apache/commons/pool2/impl/EvictionTimer.java | 4 +-
.../commons/pool2/impl/GenericKeyedObjectPool.java | 14 +-
.../commons/pool2/impl/GenericObjectPool.java | 20 +-
.../pool2/impl/SoftReferenceObjectPool.java | 2 +-
.../apache/commons/pool2/TestBaseObjectPool.java | 562 +-
.../apache/commons/pool2/TestKeyedObjectPool.java | 1540 ++---
.../org/apache/commons/pool2/WaiterFactory.java | 434 +-
.../pool2/impl/TestGenericKeyedObjectPool.java | 92 +-
.../commons/pool2/impl/TestGenericObjectPool.java | 6022 ++++++++++----------
11 files changed, 4400 insertions(+), 4400 deletions(-)
diff --git a/src/main/java/org/apache/commons/pool2/PoolUtils.java b/src/main/java/org/apache/commons/pool2/PoolUtils.java
index 4c732579..d94e4849 100644
--- a/src/main/java/org/apache/commons/pool2/PoolUtils.java
+++ b/src/main/java/org/apache/commons/pool2/PoolUtils.java
@@ -99,7 +99,7 @@ public final class PoolUtils {
idleHighWaterMark = Math.max(idle, idleHighWaterMark);
final float maxInterval = 15f;
final float minutes = maxInterval +
- ((1f - maxInterval) / idleHighWaterMark) * idle;
+ (1f - maxInterval) / idleHighWaterMark * idle;
nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor);
}
}
diff --git a/src/main/java/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java b/src/main/java/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java
index caf5dfea..5af41b18 100644
--- a/src/main/java/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java
+++ b/src/main/java/org/apache/commons/pool2/impl/DefaultEvictionPolicy.java
@@ -1,54 +1,54 @@
-/*
- * 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.impl;
-
-import org.apache.commons.pool2.PooledObject;
-
-/**
- * Provides the default implementation of {@link EvictionPolicy} used by the pools.
- * <p>
- * Objects will be evicted if the following conditions are met:
- * </p>
- * <ul>
- * <li>The object has been idle longer than
- * {@link GenericObjectPool#getMinEvictableIdleDuration()} /
- * {@link GenericKeyedObjectPool#getMinEvictableIdleDuration()}</li>
- * <li>There are more than {@link GenericObjectPool#getMinIdle()} /
- * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} idle objects in
- * the pool and the object has been idle for longer than
- * {@link GenericObjectPool#getSoftMinEvictableIdleDuration()} /
- * {@link GenericKeyedObjectPool#getSoftMinEvictableIdleDuration()}
- * </ul>
- * <p>
- * This class is immutable and thread-safe.
- * </p>
- *
- * @param <T> the type of objects in the pool.
- *
- * @since 2.0
- */
-public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {
-
- @Override
- public boolean evict(final EvictionConfig config, final PooledObject<T> underTest, final int idleCount) {
- // @formatter:off
- return (config.getIdleSoftEvictDuration().compareTo(underTest.getIdleDuration()) < 0 &&
- config.getMinIdle() < idleCount) ||
- config.getIdleEvictDuration().compareTo(underTest.getIdleDuration()) < 0;
- // @formatter:on
- }
-}
+/*
+ * 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.impl;
+
+import org.apache.commons.pool2.PooledObject;
+
+/**
+ * Provides the default implementation of {@link EvictionPolicy} used by the pools.
+ * <p>
+ * Objects will be evicted if the following conditions are met:
+ * </p>
+ * <ul>
+ * <li>The object has been idle longer than
+ * {@link GenericObjectPool#getMinEvictableIdleDuration()} /
+ * {@link GenericKeyedObjectPool#getMinEvictableIdleDuration()}</li>
+ * <li>There are more than {@link GenericObjectPool#getMinIdle()} /
+ * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} idle objects in
+ * the pool and the object has been idle for longer than
+ * {@link GenericObjectPool#getSoftMinEvictableIdleDuration()} /
+ * {@link GenericKeyedObjectPool#getSoftMinEvictableIdleDuration()}
+ * </ul>
+ * <p>
+ * This class is immutable and thread-safe.
+ * </p>
+ *
+ * @param <T> the type of objects in the pool.
+ *
+ * @since 2.0
+ */
+public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {
+
+ @Override
+ public boolean evict(final EvictionConfig config, final PooledObject<T> underTest, final int idleCount) {
+ // @formatter:off
+ return config.getIdleSoftEvictDuration().compareTo(underTest.getIdleDuration()) < 0 &&
+ config.getMinIdle() < idleCount ||
+ config.getIdleEvictDuration().compareTo(underTest.getIdleDuration()) < 0;
+ // @formatter:on
+ }
+}
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 24e162df..b99e47e7 100644
--- a/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java
+++ b/src/main/java/org/apache/commons/pool2/impl/EvictionTimer.java
@@ -126,7 +126,7 @@ class EvictionTimer {
/** Keys are weak references to tasks, values are runners managed by executor. */
private static final HashMap<
- WeakReference<BaseGenericObjectPool<?, ?>.Evictor>,
+ WeakReference<BaseGenericObjectPool<?, ?>.Evictor>,
WeakRunner<BaseGenericObjectPool<?, ?>.Evictor>> TASK_MAP = new HashMap<>(); // @GuardedBy("EvictionTimer.class")
/**
@@ -159,7 +159,7 @@ class EvictionTimer {
/**
* For testing only.
- *
+ *
* @return The executor.
*/
static ScheduledThreadPoolExecutor getExecutor() {
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 8c4e6646..55ad8881 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -416,8 +416,8 @@ public class GenericKeyedObjectPool<K, T, E extends Exception> extends BaseGener
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);
}
@@ -444,7 +444,7 @@ public class GenericKeyedObjectPool<K, T, E extends Exception> extends BaseGener
if (blockWhenExhausted) {
if (p == null) {
try {
- p = borrowMaxWaitMillis < 0 ? objectDeque.getIdleObjects().takeFirst() :
+ p = borrowMaxWaitMillis < 0 ? objectDeque.getIdleObjects().takeFirst() :
objectDeque.getIdleObjects().pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw cast(e);
@@ -604,7 +604,7 @@ public class GenericKeyedObjectPool<K, T, E extends Exception> extends BaseGener
* @param key the key to clear
* @param reuseCapacity whether or not to reuse freed capacity
*/
- public void clear(final K key, boolean reuseCapacity) {
+ public void clear(final K key, final boolean reuseCapacity) {
final ObjectDeque<T> objectDeque = register(key);
int freedCapacity = 0;
try {
@@ -653,7 +653,7 @@ public class GenericKeyedObjectPool<K, T, E extends Exception> extends BaseGener
// Now iterate created map and kill the first 15% plus one to account
// for zero
- int itemsToRemove = ((int) (map.size() * 0.15)) + 1;
+ int itemsToRemove = (int) (map.size() * 0.15) + 1;
final Iterator<Entry<PooledObject<T>, K>> iter = map.entrySet().iterator();
while (iter.hasNext() && itemsToRemove > 0) {
@@ -1237,7 +1237,7 @@ public class GenericKeyedObjectPool<K, T, E extends Exception> extends BaseGener
if (numTests >= 0) {
return Math.min(numTests, totalIdle);
}
- return (int) (Math.ceil(totalIdle / Math.abs((double) numTests)));
+ return (int) Math.ceil(totalIdle / Math.abs((double) numTests));
}
/**
@@ -1617,7 +1617,7 @@ public class GenericKeyedObjectPool<K, T, E extends Exception> extends BaseGener
*
* @param newCapacity number of new instances to attempt to create.
*/
- private void reuseCapacity(int newCapacity) {
+ private void reuseCapacity(final int newCapacity) {
for (int i = 0; i < newCapacity; i++) {
reuseCapacity();
}
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 1eb4bb22..1e105cc2 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -275,8 +275,8 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
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);
}
@@ -305,7 +305,7 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
} catch (final InterruptedException e) {
// Don't surface exception type of internal locking mechanism.
throw cast(e);
- }
+ }
}
if (p == null) {
throw new NoSuchElementException(appendStats(
@@ -547,8 +547,8 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
// Do not block more if maxWaitTimeMillis is set.
if (create == null &&
- (localMaxWaitTimeMillis > 0 &&
- System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) {
+ localMaxWaitTimeMillis > 0 &&
+ System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis) {
create = Boolean.FALSE;
}
}
@@ -620,7 +620,7 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
* @throws E if the factory's makeObject throws
*/
private void ensureIdle(final int idleCount, final boolean always) throws E {
- if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
+ if (idleCount < 1 || isClosed() || !always && !idleObjects.hasTakeWaiters()) {
return;
}
@@ -865,8 +865,8 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
if (numTestsPerEvictionRun >= 0) {
return Math.min(numTestsPerEvictionRun, idleObjects.size());
}
- return (int) (Math.ceil(idleObjects.size() /
- Math.abs((double) numTestsPerEvictionRun)));
+ return (int) Math.ceil(idleObjects.size() /
+ Math.abs((double) numTestsPerEvictionRun));
}
/**
@@ -918,7 +918,7 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
* Activation of this method decrements the active count and attempts to destroy the instance, using the provided
* {@link DestroyMode}.
* </p>
- *
+ *
* @throws E if an exception occurs destroying the object
* @throws IllegalStateException if obj does not belong to this pool
* @since 2.9.0
@@ -1177,5 +1177,5 @@ public class GenericObjectPool<T, E extends Exception> extends BaseGenericObject
getPooledObject(pooledObject).use();
}
}
-
+
}
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 5e72b09a..bc5ad180 100644
--- a/src/main/java/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/SoftReferenceObjectPool.java
@@ -354,7 +354,7 @@ public class SoftReferenceObjectPool<T, E extends Exception> extends BaseObjectP
removeClearedReferences(idleReferences.iterator());
removeClearedReferences(allReferences.iterator());
while (refQueue.poll() != null) {
- // empty
+ // draining queue
}
}
diff --git a/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java b/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java
index fb20afda..0d034c7e 100644
--- a/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java
@@ -1,281 +1,281 @@
-/*
- * 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;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import org.junit.jupiter.api.Test;
-
-/**
- */
-public class TestBaseObjectPool extends TestObjectPool {
- private static class TestObjectPool extends BaseObjectPool<Object, RuntimeException> {
-
- @Override
- public Object borrowObject() {
- return null;
- }
-
- @Override
- public void invalidateObject(final Object obj) {
- }
-
- @Override
- public void returnObject(final Object obj) {
- }
- }
-
- private ObjectPool<String, RuntimeException> pool;
-
- /**
- * @param n Ignored by this implemented. Used by sub-classes.
- *
- * @return the Nth object (zero indexed)
- */
- protected Object getNthObject(final int n) {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
- }
- throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
- }
-
- protected boolean isFifo() {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
- }
- return false;
- }
-
- protected boolean isLifo() {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
- }
- return false;
- }
-
- /**
- * @param minCapacity Ignored by this implemented. Used by sub-classes.
- *
- * @return A newly created empty pool
- */
- protected <E extends Exception> ObjectPool<String, E> makeEmptyPool(final int minCapacity) {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
- }
- throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
- }
-
- @Override
- protected <E extends Exception> ObjectPool<Object, E> makeEmptyPool(final PooledObjectFactory<Object, E> factory) {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
- }
- throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
- }
-
- @Test
- public void testBaseAddObject() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- try {
- assertEquals(0, pool.getNumIdle());
- assertEquals(0, pool.getNumActive());
- pool.addObject();
- assertEquals(1, pool.getNumIdle());
- assertEquals(0, pool.getNumActive());
- final String obj = pool.borrowObject();
- assertEquals(getNthObject(0), obj);
- assertEquals(0, pool.getNumIdle());
- assertEquals(1, pool.getNumActive());
- pool.returnObject(obj);
- assertEquals(1, pool.getNumIdle());
- assertEquals(0, pool.getNumActive());
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if one of those calls is unsupported
- } finally {
- pool.close();
- }
- }
-
- @Test
- public void testBaseBorrow() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- assertEquals(getNthObject(0), pool.borrowObject());
- assertEquals(getNthObject(1), pool.borrowObject());
- assertEquals(getNthObject(2), pool.borrowObject());
- pool.close();
- }
-
- @Test
- public void testBaseBorrowReturn() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- String obj0 = pool.borrowObject();
- assertEquals(getNthObject(0), obj0);
- String obj1 = pool.borrowObject();
- assertEquals(getNthObject(1), obj1);
- String obj2 = pool.borrowObject();
- assertEquals(getNthObject(2), obj2);
- pool.returnObject(obj2);
- obj2 = pool.borrowObject();
- assertEquals(getNthObject(2), obj2);
- pool.returnObject(obj1);
- obj1 = pool.borrowObject();
- assertEquals(getNthObject(1), obj1);
- pool.returnObject(obj0);
- pool.returnObject(obj2);
- obj2 = pool.borrowObject();
- if (isLifo()) {
- assertEquals(getNthObject(2),obj2);
- }
- if (isFifo()) {
- assertEquals(getNthObject(0),obj2);
- }
-
- obj0 = pool.borrowObject();
- if (isLifo()) {
- assertEquals(getNthObject(0),obj0);
- }
- if (isFifo()) {
- assertEquals(getNthObject(2),obj0);
- }
- pool.close();
- }
-
- @Test
- public void testBaseClear() {
- try {
- pool = makeEmptyPool(3);
- } catch (final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- assertEquals(0, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- final String obj0 = pool.borrowObject();
- final String obj1 = pool.borrowObject();
- assertEquals(2, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- pool.returnObject(obj1);
- pool.returnObject(obj0);
- assertEquals(0, pool.getNumActive());
- assertEquals(2, pool.getNumIdle());
- pool.clear();
- assertEquals(0, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- final Object obj2 = pool.borrowObject();
- assertEquals(getNthObject(2), obj2);
- pool.close();
- }
-
- @Test
- public void testBaseClosePool() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- final String obj = pool.borrowObject();
- pool.returnObject(obj);
-
- pool.close();
- assertThrows(IllegalStateException.class, pool::borrowObject);
- }
-
- @Test
- public void testBaseInvalidateObject() {
- try {
- pool = makeEmptyPool(3);
- } catch (final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- assertEquals(0, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- final String obj0 = pool.borrowObject();
- final String obj1 = pool.borrowObject();
- assertEquals(2, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- pool.invalidateObject(obj0);
- assertEquals(1, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- pool.invalidateObject(obj1);
- assertEquals(0, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- pool.close();
- }
-
- @Test
- public void testBaseNumActiveNumIdle() {
- try {
- pool = makeEmptyPool(3);
- } catch (final UnsupportedOperationException e) {
- return; // skip this test if unsupported
- }
- assertEquals(0, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- final String obj0 = pool.borrowObject();
- assertEquals(1, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- final String obj1 = pool.borrowObject();
- assertEquals(2, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
- pool.returnObject(obj1);
- assertEquals(1, pool.getNumActive());
- assertEquals(1, pool.getNumIdle());
- pool.returnObject(obj0);
- assertEquals(0, pool.getNumActive());
- assertEquals(2, pool.getNumIdle());
- pool.close();
- }
-
- @Test
- public void testClose() {
- @SuppressWarnings("resource")
- final ObjectPool<Object, RuntimeException> pool = new TestObjectPool();
-
- pool.close();
- pool.close(); // should not error as of Pool 2.0.
- }
-
- // tests
- @Test
- public void testUnsupportedOperations() {
- if (!getClass().equals(TestBaseObjectPool.class)) {
- return; // skip redundant tests
- }
- try (final ObjectPool<Object,RuntimeException> pool = new TestObjectPool()) {
-
- assertTrue( pool.getNumIdle() < 0,"Negative expected.");
- assertTrue( pool.getNumActive() < 0,"Negative expected.");
-
- assertThrows(UnsupportedOperationException.class, pool::clear);
- assertThrows(UnsupportedOperationException.class, pool::addObject);
- }
- }
-}
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ */
+public class TestBaseObjectPool extends TestObjectPool {
+ private static class TestObjectPool extends BaseObjectPool<Object, RuntimeException> {
+
+ @Override
+ public Object borrowObject() {
+ return null;
+ }
+
+ @Override
+ public void invalidateObject(final Object obj) {
+ }
+
+ @Override
+ public void returnObject(final Object obj) {
+ }
+ }
+
+ private ObjectPool<String, RuntimeException> pool;
+
+ /**
+ * @param n Ignored by this implemented. Used by sub-classes.
+ *
+ * @return the Nth object (zero indexed)
+ */
+ protected Object getNthObject(final int n) {
+ if (this.getClass() != TestBaseObjectPool.class) {
+ fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ }
+ throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
+ }
+
+ protected boolean isFifo() {
+ if (this.getClass() != TestBaseObjectPool.class) {
+ fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ }
+ return false;
+ }
+
+ protected boolean isLifo() {
+ if (this.getClass() != TestBaseObjectPool.class) {
+ fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ }
+ return false;
+ }
+
+ /**
+ * @param minCapacity Ignored by this implemented. Used by sub-classes.
+ *
+ * @return A newly created empty pool
+ */
+ protected <E extends Exception> ObjectPool<String, E> makeEmptyPool(final int minCapacity) {
+ if (this.getClass() != TestBaseObjectPool.class) {
+ fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ }
+ throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
+ }
+
+ @Override
+ protected <E extends Exception> ObjectPool<Object, E> makeEmptyPool(final PooledObjectFactory<Object, E> factory) {
+ if (this.getClass() != TestBaseObjectPool.class) {
+ fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ }
+ throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
+ }
+
+ @Test
+ public void testBaseAddObject() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ try {
+ assertEquals(0, pool.getNumIdle());
+ assertEquals(0, pool.getNumActive());
+ pool.addObject();
+ assertEquals(1, pool.getNumIdle());
+ assertEquals(0, pool.getNumActive());
+ final String obj = pool.borrowObject();
+ assertEquals(getNthObject(0), obj);
+ assertEquals(0, pool.getNumIdle());
+ assertEquals(1, pool.getNumActive());
+ pool.returnObject(obj);
+ assertEquals(1, pool.getNumIdle());
+ assertEquals(0, pool.getNumActive());
+ } catch(final UnsupportedOperationException e) {
+ return; // skip this test if one of those calls is unsupported
+ } finally {
+ pool.close();
+ }
+ }
+
+ @Test
+ public void testBaseBorrow() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ assertEquals(getNthObject(0), pool.borrowObject());
+ assertEquals(getNthObject(1), pool.borrowObject());
+ assertEquals(getNthObject(2), pool.borrowObject());
+ pool.close();
+ }
+
+ @Test
+ public void testBaseBorrowReturn() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ String obj0 = pool.borrowObject();
+ assertEquals(getNthObject(0), obj0);
+ String obj1 = pool.borrowObject();
+ assertEquals(getNthObject(1), obj1);
+ String obj2 = pool.borrowObject();
+ assertEquals(getNthObject(2), obj2);
+ pool.returnObject(obj2);
+ obj2 = pool.borrowObject();
+ assertEquals(getNthObject(2), obj2);
+ pool.returnObject(obj1);
+ obj1 = pool.borrowObject();
+ assertEquals(getNthObject(1), obj1);
+ pool.returnObject(obj0);
+ pool.returnObject(obj2);
+ obj2 = pool.borrowObject();
+ if (isLifo()) {
+ assertEquals(getNthObject(2),obj2);
+ }
+ if (isFifo()) {
+ assertEquals(getNthObject(0),obj2);
+ }
+
+ obj0 = pool.borrowObject();
+ if (isLifo()) {
+ assertEquals(getNthObject(0),obj0);
+ }
+ if (isFifo()) {
+ assertEquals(getNthObject(2),obj0);
+ }
+ pool.close();
+ }
+
+ @Test
+ public void testBaseClear() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch (final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ assertEquals(0, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ final String obj0 = pool.borrowObject();
+ final String obj1 = pool.borrowObject();
+ assertEquals(2, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ pool.returnObject(obj1);
+ pool.returnObject(obj0);
+ assertEquals(0, pool.getNumActive());
+ assertEquals(2, pool.getNumIdle());
+ pool.clear();
+ assertEquals(0, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ final Object obj2 = pool.borrowObject();
+ assertEquals(getNthObject(2), obj2);
+ pool.close();
+ }
+
+ @Test
+ public void testBaseClosePool() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ final String obj = pool.borrowObject();
+ pool.returnObject(obj);
+
+ pool.close();
+ assertThrows(IllegalStateException.class, pool::borrowObject);
+ }
+
+ @Test
+ public void testBaseInvalidateObject() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch (final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ assertEquals(0, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ final String obj0 = pool.borrowObject();
+ final String obj1 = pool.borrowObject();
+ assertEquals(2, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ pool.invalidateObject(obj0);
+ assertEquals(1, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ pool.invalidateObject(obj1);
+ assertEquals(0, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ pool.close();
+ }
+
+ @Test
+ public void testBaseNumActiveNumIdle() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch (final UnsupportedOperationException e) {
+ return; // skip this test if unsupported
+ }
+ assertEquals(0, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ final String obj0 = pool.borrowObject();
+ assertEquals(1, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ final String obj1 = pool.borrowObject();
+ assertEquals(2, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+ pool.returnObject(obj1);
+ assertEquals(1, pool.getNumActive());
+ assertEquals(1, pool.getNumIdle());
+ pool.returnObject(obj0);
+ assertEquals(0, pool.getNumActive());
+ assertEquals(2, pool.getNumIdle());
+ pool.close();
+ }
+
+ @Test
+ public void testClose() {
+ @SuppressWarnings("resource")
+ final ObjectPool<Object, RuntimeException> pool = new TestObjectPool();
+
+ pool.close();
+ pool.close(); // should not error as of Pool 2.0.
+ }
+
+ // tests
+ @Test
+ public void testUnsupportedOperations() {
+ if (!getClass().equals(TestBaseObjectPool.class)) {
+ return; // skip redundant tests
+ }
+ try (final ObjectPool<Object,RuntimeException> pool = new TestObjectPool()) {
+
+ assertTrue( pool.getNumIdle() < 0,"Negative expected.");
+ assertTrue( pool.getNumActive() < 0,"Negative expected.");
+
+ assertThrows(UnsupportedOperationException.class, pool::clear);
+ assertThrows(UnsupportedOperationException.class, pool::addObject);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java b/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java
index 7f558fb5..aec7c0fb 100644
--- a/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java
@@ -1,770 +1,770 @@
-/*
- * 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;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-/**
- * Abstract test case for {@link ObjectPool} implementations.
- */
-public abstract class TestKeyedObjectPool {
-
- protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory<Object, Object, PrivateException> {
- private final List<MethodCall> methodCalls = new ArrayList<>();
- private int count;
- private boolean makeObjectFail;
- private boolean activateObjectFail;
- private boolean validateObjectFail;
- private boolean passivateObjectFail;
- private boolean destroyObjectFail;
-
- public FailingKeyedPooledObjectFactory() {
- }
-
- @Override
- public void activateObject(final Object key, final PooledObject<Object> obj) {
- methodCalls.add(new MethodCall("activateObject", key, obj.getObject()));
- if (activateObjectFail) {
- throw new PrivateException("activateObject");
- }
- }
-
- @Override
- public void destroyObject(final Object key, final PooledObject<Object> obj) {
- methodCalls.add(new MethodCall("destroyObject", key, obj.getObject()));
- if (destroyObjectFail) {
- throw new PrivateException("destroyObject");
- }
- }
-
- public int getCurrentCount() {
- return count;
- }
-
- public List<MethodCall> getMethodCalls() {
- return methodCalls;
- }
-
- public boolean isActivateObjectFail() {
- return activateObjectFail;
- }
-
- public boolean isDestroyObjectFail() {
- return destroyObjectFail;
- }
-
- public boolean isMakeObjectFail() {
- return makeObjectFail;
- }
-
- public boolean isPassivateObjectFail() {
- return passivateObjectFail;
- }
-
- public boolean isValidateObjectFail() {
- return validateObjectFail;
- }
-
- @Override
- public PooledObject<Object> makeObject(final Object key) {
- final MethodCall call = new MethodCall("makeObject", key);
- methodCalls.add(call);
- final int originalCount = this.count++;
- if (makeObjectFail) {
- throw new PrivateException("makeObject");
- }
- // Deliberate choice to create new object in case future unit test
- // checks for a specific object
- final Integer obj = Integer.valueOf(originalCount);
- call.setReturned(obj);
- return new DefaultPooledObject<>(obj);
- }
-
- @Override
- public void passivateObject(final Object key, final PooledObject<Object> obj) {
- methodCalls.add(new MethodCall("passivateObject", key, obj.getObject()));
- if (passivateObjectFail) {
- throw new PrivateException("passivateObject");
- }
- }
-
- public void reset() {
- count = 0;
- getMethodCalls().clear();
- setMakeObjectFail(false);
- setActivateObjectFail(false);
- setValidateObjectFail(false);
- setPassivateObjectFail(false);
- setDestroyObjectFail(false);
- }
-
- public void setActivateObjectFail(final boolean activateObjectFail) {
- this.activateObjectFail = activateObjectFail;
- }
-
- public void setCurrentCount(final int count) {
- this.count = count;
- }
-
- public void setDestroyObjectFail(final boolean destroyObjectFail) {
- this.destroyObjectFail = destroyObjectFail;
- }
-
- public void setMakeObjectFail(final boolean makeObjectFail) {
- this.makeObjectFail = makeObjectFail;
- }
-
- public void setPassivateObjectFail(final boolean passivateObjectFail) {
- this.passivateObjectFail = passivateObjectFail;
- }
-
- public void setValidateObjectFail(final boolean validateObjectFail) {
- this.validateObjectFail = validateObjectFail;
- }
-
- @Override
- public boolean validateObject(final Object key, final PooledObject<Object> obj) {
- final MethodCall call = new MethodCall("validateObject", key, obj.getObject());
- methodCalls.add(call);
- if (validateObjectFail) {
- throw new PrivateException("validateObject");
- }
- final boolean r = true;
- call.returned(Boolean.valueOf(r));
- return r;
- }
- }
-
- private static class TestFactory extends BaseKeyedPooledObjectFactory<Object, Object, RuntimeException> {
- @Override
- public Object create(final Object key) {
- return new Object();
- }
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
- }
- }
-
- protected static final String KEY = "key";
-
- private KeyedObjectPool<Object, Object, RuntimeException> pool;
-
- // Deliberate choice to create a new object in case future unit tests check
- // for a specific object.
- private final Integer ZERO = Integer.valueOf(0);
-
- private final Integer ONE = Integer.valueOf(1);
-
- private void clear(final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) {
- factory.getMethodCalls().clear();
- expectedMethods.clear();
- }
-
- /**
- * Return what we expect to be the n<sup>th</sup>
- * object (zero indexed) created by the pool
- * for the given key.
- * @param key Key for the object to be obtained
- * @param n index of the object to be obtained
- *
- * @return the requested object
- */
- protected abstract Object getNthObject(Object key, int n);
-
- protected abstract boolean isFifo();
-
- protected abstract boolean isLifo();
-
- /**
- * Creates an {@link KeyedObjectPool} instance
- * that can contain at least <i>minCapacity</i>
- * idle and active objects, or
- * throw {@link IllegalArgumentException}
- * if such a pool cannot be created.
- * @param minCapacity Minimum capacity of the pool to create
- *
- * @return the newly created keyed object pool
- */
- protected abstract <E extends Exception> KeyedObjectPool<Object, Object, E> makeEmptyPool(int minCapacity);
-
- /**
- * Creates an {@code KeyedObjectPool} with the specified factory.
- * The pool should be in a default configuration and conform to the expected
- * behaviors described in {@link KeyedObjectPool}.
- * Generally speaking there should be no limits on the various object counts.
- *
- * @param <E> The type of exception thrown by the pool
- * @param factory Factory to use to associate with the pool
- * @return The newly created empty pool
- */
- protected abstract <E extends Exception> KeyedObjectPool<Object, Object, E> makeEmptyPool(KeyedPooledObjectFactory<Object, Object, E> factory);
-
- protected abstract Object makeKey(int n);
-
- private <E extends Exception> void reset(final KeyedObjectPool<Object, Object, E> pool, final FailingKeyedPooledObjectFactory factory,
- final List<MethodCall> expectedMethods) throws E {
- pool.clear();
- clear(factory, expectedMethods);
- factory.reset();
- }
-
- @AfterEach
- public void tearDown() {
- pool = null;
- }
-
- @Test
- public void testBaseAddObject() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object key = makeKey(0);
- try {
- assertEquals(0,pool.getNumIdle());
- assertEquals(0,pool.getNumActive());
- assertEquals(0,pool.getNumIdle(key));
- assertEquals(0,pool.getNumActive(key));
- pool.addObject(key);
- assertEquals(1,pool.getNumIdle());
- assertEquals(0,pool.getNumActive());
- assertEquals(1,pool.getNumIdle(key));
- assertEquals(0,pool.getNumActive(key));
- final Object obj = pool.borrowObject(key);
- assertEquals(getNthObject(key,0),obj);
- assertEquals(0,pool.getNumIdle());
- assertEquals(1,pool.getNumActive());
- assertEquals(0,pool.getNumIdle(key));
- assertEquals(1,pool.getNumActive(key));
- pool.returnObject(key,obj);
- assertEquals(1,pool.getNumIdle());
- assertEquals(0,pool.getNumActive());
- assertEquals(1,pool.getNumIdle(key));
- assertEquals(0,pool.getNumActive(key));
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if one of those calls is unsupported
- } finally {
- pool.close();
- }
- }
-
- @Test
- public void testBaseBorrow() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object keya = makeKey(0);
- final Object keyb = makeKey(1);
- assertEquals(getNthObject(keya,0),pool.borrowObject(keya),"1");
- assertEquals(getNthObject(keyb,0),pool.borrowObject(keyb),"2");
- assertEquals(getNthObject(keyb,1),pool.borrowObject(keyb),"3");
- assertEquals(getNthObject(keya,1),pool.borrowObject(keya),"4");
- assertEquals(getNthObject(keyb,2),pool.borrowObject(keyb),"5");
- assertEquals(getNthObject(keya,2),pool.borrowObject(keya),"6");
- pool.close();
- }
-
- @Test
- public void testBaseBorrowReturn() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object keya = makeKey(0);
- Object obj0 = pool.borrowObject(keya);
- assertEquals(getNthObject(keya,0),obj0);
- Object obj1 = pool.borrowObject(keya);
- assertEquals(getNthObject(keya,1),obj1);
- Object obj2 = pool.borrowObject(keya);
- assertEquals(getNthObject(keya,2),obj2);
- pool.returnObject(keya,obj2);
- obj2 = pool.borrowObject(keya);
- assertEquals(getNthObject(keya,2),obj2);
- pool.returnObject(keya,obj1);
- obj1 = pool.borrowObject(keya);
- assertEquals(getNthObject(keya,1),obj1);
- pool.returnObject(keya,obj0);
- pool.returnObject(keya,obj2);
- obj2 = pool.borrowObject(keya);
- if (isLifo()) {
- assertEquals(getNthObject(keya,2),obj2);
- }
- if (isFifo()) {
- assertEquals(getNthObject(keya,0),obj2);
- }
- obj0 = pool.borrowObject(keya);
- if (isLifo()) {
- assertEquals(getNthObject(keya,0),obj0);
- }
- if (isFifo()) {
- assertEquals(getNthObject(keya,2),obj0);
- }
- pool.close();
- }
-
- @Test
- public void testBaseClear() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object keya = makeKey(0);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- final Object obj0 = pool.borrowObject(keya);
- final Object obj1 = pool.borrowObject(keya);
- assertEquals(2,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- pool.returnObject(keya,obj1);
- pool.returnObject(keya,obj0);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(2,pool.getNumIdle(keya));
- pool.clear(keya);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- final Object obj2 = pool.borrowObject(keya);
- assertEquals(getNthObject(keya,2),obj2);
- pool.close();
- }
-
- @Test
- public void testBaseInvalidateObject() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object keya = makeKey(0);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- final Object obj0 = pool.borrowObject(keya);
- final Object obj1 = pool.borrowObject(keya);
- assertEquals(2,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- pool.invalidateObject(keya,obj0);
- assertEquals(1,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- pool.invalidateObject(keya,obj1);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- pool.close();
- }
-
- @Test
- public void testBaseNumActiveNumIdle() {
- try {
- pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object keya = makeKey(0);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- final Object obj0 = pool.borrowObject(keya);
- assertEquals(1,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- final Object obj1 = pool.borrowObject(keya);
- assertEquals(2,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- pool.returnObject(keya,obj1);
- assertEquals(1,pool.getNumActive(keya));
- assertEquals(1,pool.getNumIdle(keya));
- pool.returnObject(keya,obj0);
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(2,pool.getNumIdle(keya));
-
- assertEquals(0,pool.getNumActive("xyzzy12345"));
- assertEquals(0,pool.getNumIdle("xyzzy12345"));
-
- pool.close();
- }
-
- @Test
- public void testBaseNumActiveNumIdle2() {
- try {
- pool = makeEmptyPool(6);
- } catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
- }
- final Object keya = makeKey(0);
- final Object keyb = makeKey(1);
- assertEquals(0,pool.getNumActive());
- assertEquals(0,pool.getNumIdle());
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- assertEquals(0,pool.getNumActive(keyb));
- assertEquals(0,pool.getNumIdle(keyb));
-
- final Object objA0 = pool.borrowObject(keya);
- final Object objB0 = pool.borrowObject(keyb);
-
- assertEquals(2,pool.getNumActive());
- assertEquals(0,pool.getNumIdle());
- assertEquals(1,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- assertEquals(1,pool.getNumActive(keyb));
- assertEquals(0,pool.getNumIdle(keyb));
-
- final Object objA1 = pool.borrowObject(keya);
- final Object objB1 = pool.borrowObject(keyb);
-
- assertEquals(4,pool.getNumActive());
- assertEquals(0,pool.getNumIdle());
- assertEquals(2,pool.getNumActive(keya));
- assertEquals(0,pool.getNumIdle(keya));
- assertEquals(2,pool.getNumActive(keyb));
- assertEquals(0,pool.getNumIdle(keyb));
-
- pool.returnObject(keya,objA0);
- pool.returnObject(keyb,objB0);
-
- assertEquals(2,pool.getNumActive());
- assertEquals(2,pool.getNumIdle());
- assertEquals(1,pool.getNumActive(keya));
- assertEquals(1,pool.getNumIdle(keya));
- assertEquals(1,pool.getNumActive(keyb));
- assertEquals(1,pool.getNumIdle(keyb));
-
- pool.returnObject(keya,objA1);
- pool.returnObject(keyb,objB1);
-
- assertEquals(0,pool.getNumActive());
- assertEquals(4,pool.getNumIdle());
- assertEquals(0,pool.getNumActive(keya));
- assertEquals(2,pool.getNumIdle(keya));
- assertEquals(0,pool.getNumActive(keyb));
- assertEquals(2,pool.getNumIdle(keyb));
-
- pool.close();
- }
-
- @Test
- public void testClosedPoolBehavior() {
- final KeyedObjectPool<Object, Object, RuntimeException> pool;
- try {
- pool = makeEmptyPool(new TestFactory());
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
-
- final Object o1 = pool.borrowObject(KEY);
- final Object o2 = pool.borrowObject(KEY);
-
- pool.close();
-
- assertThrows(IllegalStateException.class, () -> pool.addObject(KEY),
- "A closed pool must throw an IllegalStateException when addObject is called.");
-
- assertThrows(IllegalStateException.class, () -> pool.borrowObject(KEY),
- "A closed pool must throw an IllegalStateException when borrowObject is called.");
-
- // The following should not throw exceptions just because the pool is closed.
- assertEquals( 0, pool.getNumIdle(KEY),"A closed pool shouldn't have any idle objects.");
- assertEquals( 0, pool.getNumIdle(),"A closed pool shouldn't have any idle objects.");
- pool.getNumActive();
- pool.getNumActive(KEY);
- pool.returnObject(KEY, o1);
- assertEquals( 0, pool.getNumIdle(KEY),"returnObject should not add items back into the idle object pool for a closed pool.");
- assertEquals( 0, pool.getNumIdle(),"returnObject should not add items back into the idle object pool for a closed pool.");
- pool.invalidateObject(KEY, o2);
- pool.clear(KEY);
- pool.clear();
- pool.close();
- }
-
- @Test
- public void testKPOFAddObjectUsage() {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object, Object, PrivateException> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
-
- // addObject should make a new object, passivate it and put it in the pool
- pool.addObject(KEY);
- expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO));
- expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO));
- assertEquals(expectedMethods, factory.getMethodCalls());
-
- // Test exception handling of addObject
- reset(pool, factory, expectedMethods);
-
- // makeObject Exceptions should be propagated to client code from addObject
- factory.setMakeObjectFail(true);
- assertThrows(PrivateException.class, () -> pool.addObject(KEY), "Expected addObject to propagate makeObject exception.");
- expectedMethods.add(new MethodCall("makeObject", KEY));
- assertEquals(expectedMethods, factory.getMethodCalls());
-
- clear(factory, expectedMethods);
-
- // passivateObject Exceptions should be propagated to client code from addObject
- factory.setMakeObjectFail(false);
- factory.setPassivateObjectFail(true);
- assertThrows(PrivateException.class, () -> pool.addObject(KEY), "Expected addObject to propagate passivateObject exception.");
- expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
- expectedMethods.add(new MethodCall("passivateObject", KEY, ONE));
- assertEquals(expectedMethods, factory.getMethodCalls());
- pool.close();
- }
-
- @Test
- public void testKPOFBorrowObjectUsages() {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object, Object, PrivateException> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
- Object obj;
-
- if (pool instanceof GenericKeyedObjectPool) {
- ((GenericKeyedObjectPool<Object, Object, PrivateException>) pool).setTestOnBorrow(true);
- }
-
- // Test correct behavior code paths
-
- // existing idle object should be activated and validated
- pool.addObject(KEY);
- clear(factory, expectedMethods);
- obj = pool.borrowObject(KEY);
- expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
- expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE));
- assertEquals(expectedMethods, factory.getMethodCalls());
- pool.returnObject(KEY, obj);
-
- // Test exception handling of borrowObject
- reset(pool, factory, expectedMethods);
-
- // makeObject Exceptions should be propagated to client code from borrowObject
- factory.setMakeObjectFail(true);
- try {
- obj = pool.borrowObject(KEY);
- fail("Expected borrowObject to propagate makeObject exception.");
- } catch (final PrivateException pe) {
- // expected
- }
- expectedMethods.add(new MethodCall("makeObject", KEY));
- assertEquals(expectedMethods, factory.getMethodCalls());
-
-
- // when activateObject fails in borrowObject, a new object should be borrowed/created
- reset(pool, factory, expectedMethods);
- pool.addObject(KEY);
- clear(factory, expectedMethods);
-
- factory.setActivateObjectFail(true);
- expectedMethods.add(new MethodCall("activateObject", KEY, obj));
- assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
- // After idle object fails validation, new on is created and activation
- // fails again for the new one.
- expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
- expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
- TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
- assertEquals(expectedMethods, factory.getMethodCalls());
-
- // when validateObject fails in borrowObject, a new object should be borrowed/created
- reset(pool, factory, expectedMethods);
- pool.addObject(KEY);
- clear(factory, expectedMethods);
-
- factory.setValidateObjectFail(true);
- // testOnBorrow is on, so this will throw when the newly created instance
- // fails validation
- assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
- // Activate, then validate for idle instance
- expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
- expectedMethods.add(new MethodCall("validateObject", KEY, ZERO));
- // Make new instance, activate succeeds, validate fails
- expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
- expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
- expectedMethods.add(new MethodCall("validateObject", KEY, ONE));
- TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
- assertEquals(expectedMethods, factory.getMethodCalls());
- pool.close();
- }
-
- @Test
- public void testKPOFClearUsages() {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object, Object, PrivateException> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
-
- // Test correct behavior code paths
- pool.addObjects(KEY, 5);
- pool.clear();
-
- // Test exception handling clear should swallow destroy object failures
- reset(pool, factory, expectedMethods);
- factory.setDestroyObjectFail(true);
- pool.addObjects(KEY, 5);
- pool.clear();
- pool.close();
- }
-
-
- @Test
- public void testKPOFCloseUsages() {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- KeyedObjectPool<Object, Object, PrivateException> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch (final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
-
- // Test correct behavior code paths
- pool.addObjects(KEY, 5);
- pool.close();
-
- // Test exception handling close should swallow failures
- try (final KeyedObjectPool<Object, Object, PrivateException> pool2 = makeEmptyPool(factory)) {
- reset(pool2, factory, expectedMethods);
- factory.setDestroyObjectFail(true);
- pool2.addObjects(KEY, 5);
- }
- }
-
- @Test
- public void testKPOFInvalidateObjectUsages() throws InterruptedException {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object, Object, PrivateException> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
- Object obj;
-
- // Test correct behavior code paths
-
- obj = pool.borrowObject(KEY);
- clear(factory, expectedMethods);
-
- // invalidated object should be destroyed
- pool.invalidateObject(KEY, obj);
- expectedMethods.add(new MethodCall("destroyObject", KEY, obj));
- assertEquals(expectedMethods, factory.getMethodCalls());
-
- // Test exception handling of invalidateObject
- reset(pool, factory, expectedMethods);
- final Object obj2 = pool.borrowObject(KEY);
- clear(factory, expectedMethods);
- factory.setDestroyObjectFail(true);
- assertThrows(PrivateException.class, () -> pool.invalidateObject(KEY, obj2), "Expecting destroy exception to propagate");
- Thread.sleep(250); // could be defered
- TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
- assertEquals(expectedMethods, factory.getMethodCalls());
- pool.close();
- }
-
- @Test
- public void testKPOFReturnObjectUsages() {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object, Object, PrivateException> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
- Object obj;
-
- // Test correct behavior code paths
- obj = pool.borrowObject(KEY);
- clear(factory, expectedMethods);
-
- // returned object should be passivated
- pool.returnObject(KEY, obj);
- expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
- assertEquals(expectedMethods, factory.getMethodCalls());
-
- // Test exception handling of returnObject
- reset(pool, factory, expectedMethods);
-
- // passivateObject should swallow exceptions and not add the object to the pool
- pool.addObject(KEY);
- pool.addObject(KEY);
- pool.addObject(KEY);
- assertEquals(3, pool.getNumIdle(KEY));
- obj = pool.borrowObject(KEY);
- obj = pool.borrowObject(KEY);
- assertEquals(1, pool.getNumIdle(KEY));
- assertEquals(2, pool.getNumActive(KEY));
- clear(factory, expectedMethods);
- factory.setPassivateObjectFail(true);
- pool.returnObject(KEY, obj);
- expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
- TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
- assertEquals(expectedMethods, factory.getMethodCalls());
- assertEquals(1, pool.getNumIdle(KEY)); // Not added
- assertEquals(1, pool.getNumActive(KEY)); // But not active
-
- reset(pool, factory, expectedMethods);
- obj = pool.borrowObject(KEY);
- clear(factory, expectedMethods);
- factory.setPassivateObjectFail(true);
- factory.setDestroyObjectFail(true);
- try {
- pool.returnObject(KEY, obj);
- if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat
- fail("Expecting destroyObject exception to be propagated");
- }
- } catch (final PrivateException ex) {
- // Expected
- }
- pool.close();
- }
-
- @Test
- public void testToString() {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- try (final KeyedObjectPool<Object, Object, PrivateException> pool = makeEmptyPool(factory)) {
- pool.toString();
- } catch (final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- }
-}
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Abstract test case for {@link ObjectPool} implementations.
+ */
+public abstract class TestKeyedObjectPool {
+
+ protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory<Object, Object, PrivateException> {
+ private final List<MethodCall> methodCalls = new ArrayList<>();
+ private int count;
+ private boolean makeObjectFail;
+ private boolean activateObjectFail;
+ private boolean validateObjectFail;
+ private boolean passivateObjectFail;
+ private boolean destroyObjectFail;
+
+ public FailingKeyedPooledObjectFactory() {
+ }
+
+ @Override
+ public void activateObject(final Object key, final PooledObject<Object> obj) {
+ methodCalls.add(new MethodCall("activateObject", key, obj.getObject()));
+ if (activateObjectFail) {
+ throw new PrivateException("activateObject");
+ }
+ }
+
+ @Override
+ public void destroyObject(final Object key, final PooledObject<Object> obj) {
+ methodCalls.add(new MethodCall("destroyObject", key, obj.getObject()));
+ if (destroyObjectFail) {
+ throw new PrivateException("destroyObject");
+ }
+ }
+
+ public int getCurrentCount() {
+ return count;
+ }
+
+ public List<MethodCall> getMethodCalls() {
+ return methodCalls;
+ }
+
+ public boolean isActivateObjectFail() {
+ return activateObjectFail;
+ }
+
+ public boolean isDestroyObjectFail() {
+ return destroyObjectFail;
+ }
+
+ public boolean isMakeObjectFail() {
+ return makeObjectFail;
+ }
+
+ public boolean isPassivateObjectFail() {
+ return passivateObjectFail;
+ }
+
+ public boolean isValidateObjectFail() {
+ return validateObjectFail;
+ }
+
+ @Override
+ public PooledObject<Object> makeObject(final Object key) {
+ final MethodCall call = new MethodCall("makeObject", key);
+ methodCalls.add(call);
+ final int originalCount = this.count++;
+ if (makeObjectFail) {
+ throw new PrivateException("makeObject");
+ }
+ // Deliberate choice to create new object in case future unit test
+ // checks for a specific object
+ final Integer obj = Integer.valueOf(originalCount);
+ call.setReturned(obj);
+ return new DefaultPooledObject<>(obj);
+ }
+
+ @Override
+ public void passivateObject(final Object key, final PooledObject<Object> obj) {
+ methodCalls.add(new MethodCall("passivateObject", key, obj.getObject()));
+ if (passivateObjectFail) {
+ throw new PrivateException("passivateObject");
+ }
+ }
+
+ public void reset() {
+ count = 0;
+ getMethodCalls().clear();
+ setMakeObjectFail(false);
+ setActivateObjectFail(false);
+ setValidateObjectFail(false);
+ setPassivateObjectFail(false);
+ setDestroyObjectFail(false);
+ }
+
+ public void setActivateObjectFail(final boolean activateObjectFail) {
+ this.activateObjectFail = activateObjectFail;
+ }
+
+ public void setCurrentCount(final int count) {
+ this.count = count;
+ }
+
+ public void setDestroyObjectFail(final boolean destroyObjectFail) {
+ this.destroyObjectFail = destroyObjectFail;
+ }
+
+ public void setMakeObjectFail(final boolean makeObjectFail) {
+ this.makeObjectFail = makeObjectFail;
+ }
+
+ public void setPassivateObjectFail(final boolean passivateObjectFail) {
+ this.passivateObjectFail = passivateObjectFail;
+ }
+
+ public void setValidateObjectFail(final boolean validateObjectFail) {
+ this.validateObjectFail = validateObjectFail;
+ }
+
+ @Override
+ public boolean validateObject(final Object key, final PooledObject<Object> obj) {
+ final MethodCall call = new MethodCall("validateObject", key, obj.getObject());
+ methodCalls.add(call);
+ if (validateObjectFail) {
+ throw new PrivateException("validateObject");
+ }
+ final boolean r = true;
+ call.returned(Boolean.valueOf(r));
+ return r;
+ }
+ }
+
+ private static class TestFactory extends BaseKeyedPooledObjectFactory<Object, Object, RuntimeException> {
+ @Override
+ public Object create(final Object key) {
+ return new Object();
+ }
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
+ protected static final String KEY = "key";
+
+ private KeyedObjectPool<Object, Object, RuntimeException> pool;
+
+ // Deliberate choice to create a new object in case future unit tests check
+ // for a specific object.
+ private final Integer ZERO = Integer.valueOf(0);
+
+ private final Integer ONE = Integer.valueOf(1);
+
+ private void clear(final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) {
+ factory.getMethodCalls().clear();
+ expectedMethods.clear();
+ }
+
+ /**
+ * Return what we expect to be the n<sup>th</sup>
+ * object (zero indexed) created by the pool
+ * for the given key.
+ * @param key Key for the object to be obtained
+ * @param n index of the object to be obtained
+ *
+ * @return the requested object
+ */
+ protected abstract Object getNthObject(Object key, int n);
+
+ protected abstract boolean isFifo();
+
+ protected abstract boolean isLifo();
+
+ /**
+ * Creates an {@link KeyedObjectPool} instance
+ * that can contain at least <i>minCapacity</i>
+ * idle and active objects, or
+ * throw {@link IllegalArgumentException}
+ * if such a pool cannot be created.
+ * @param minCapacity Minimum capacity of the pool to create
+ *
+ * @return the newly created keyed object pool
+ */
+ protected abstract <E extends Exception> KeyedObjectPool<Object, Object, E> makeEmptyPool(int minCapacity);
+
+ /**
+ * Creates an {@code KeyedObjectPool} with the specified factory.
+ * The pool should be in a default configuration and conform to the expected
+ * behaviors described in {@link KeyedObjectPool}.
+ * Generally speaking there should be no limits on the various object counts.
+ *
+ * @param <E> The type of exception thrown by the pool
+ * @param factory Factory to use to associate with the pool
+ * @return The newly created empty pool
+ */
+ protected abstract <E extends Exception> KeyedObjectPool<Object, Object, E> makeEmptyPool(KeyedPooledObjectFactory<Object, Object, E> factory);
+
+ protected abstract Object makeKey(int n);
+
+ private <E extends Exception> void reset(final KeyedObjectPool<Object, Object, E> pool, final FailingKeyedPooledObjectFactory factory,
+ final List<MethodCall> expectedMethods) throws E {
+ pool.clear();
+ clear(factory, expectedMethods);
+ factory.reset();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ pool = null;
+ }
+
+ @Test
+ public void testBaseAddObject() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object key = makeKey(0);
+ try {
+ assertEquals(0,pool.getNumIdle());
+ assertEquals(0,pool.getNumActive());
+ assertEquals(0,pool.getNumIdle(key));
+ assertEquals(0,pool.getNumActive(key));
+ pool.addObject(key);
+ assertEquals(1,pool.getNumIdle());
+ assertEquals(0,pool.getNumActive());
+ assertEquals(1,pool.getNumIdle(key));
+ assertEquals(0,pool.getNumActive(key));
+ final Object obj = pool.borrowObject(key);
+ assertEquals(getNthObject(key,0),obj);
+ assertEquals(0,pool.getNumIdle());
+ assertEquals(1,pool.getNumActive());
+ assertEquals(0,pool.getNumIdle(key));
+ assertEquals(1,pool.getNumActive(key));
+ pool.returnObject(key,obj);
+ assertEquals(1,pool.getNumIdle());
+ assertEquals(0,pool.getNumActive());
+ assertEquals(1,pool.getNumIdle(key));
+ assertEquals(0,pool.getNumActive(key));
+ } catch(final UnsupportedOperationException e) {
+ return; // skip this test if one of those calls is unsupported
+ } finally {
+ pool.close();
+ }
+ }
+
+ @Test
+ public void testBaseBorrow() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object keya = makeKey(0);
+ final Object keyb = makeKey(1);
+ assertEquals(getNthObject(keya,0),pool.borrowObject(keya),"1");
+ assertEquals(getNthObject(keyb,0),pool.borrowObject(keyb),"2");
+ assertEquals(getNthObject(keyb,1),pool.borrowObject(keyb),"3");
+ assertEquals(getNthObject(keya,1),pool.borrowObject(keya),"4");
+ assertEquals(getNthObject(keyb,2),pool.borrowObject(keyb),"5");
+ assertEquals(getNthObject(keya,2),pool.borrowObject(keya),"6");
+ pool.close();
+ }
+
+ @Test
+ public void testBaseBorrowReturn() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object keya = makeKey(0);
+ Object obj0 = pool.borrowObject(keya);
+ assertEquals(getNthObject(keya,0),obj0);
+ Object obj1 = pool.borrowObject(keya);
+ assertEquals(getNthObject(keya,1),obj1);
+ Object obj2 = pool.borrowObject(keya);
+ assertEquals(getNthObject(keya,2),obj2);
+ pool.returnObject(keya,obj2);
+ obj2 = pool.borrowObject(keya);
+ assertEquals(getNthObject(keya,2),obj2);
+ pool.returnObject(keya,obj1);
+ obj1 = pool.borrowObject(keya);
+ assertEquals(getNthObject(keya,1),obj1);
+ pool.returnObject(keya,obj0);
+ pool.returnObject(keya,obj2);
+ obj2 = pool.borrowObject(keya);
+ if (isLifo()) {
+ assertEquals(getNthObject(keya,2),obj2);
+ }
+ if (isFifo()) {
+ assertEquals(getNthObject(keya,0),obj2);
+ }
+ obj0 = pool.borrowObject(keya);
+ if (isLifo()) {
+ assertEquals(getNthObject(keya,0),obj0);
+ }
+ if (isFifo()) {
+ assertEquals(getNthObject(keya,2),obj0);
+ }
+ pool.close();
+ }
+
+ @Test
+ public void testBaseClear() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object keya = makeKey(0);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ final Object obj0 = pool.borrowObject(keya);
+ final Object obj1 = pool.borrowObject(keya);
+ assertEquals(2,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ pool.returnObject(keya,obj1);
+ pool.returnObject(keya,obj0);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(2,pool.getNumIdle(keya));
+ pool.clear(keya);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ final Object obj2 = pool.borrowObject(keya);
+ assertEquals(getNthObject(keya,2),obj2);
+ pool.close();
+ }
+
+ @Test
+ public void testBaseInvalidateObject() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object keya = makeKey(0);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ final Object obj0 = pool.borrowObject(keya);
+ final Object obj1 = pool.borrowObject(keya);
+ assertEquals(2,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ pool.invalidateObject(keya,obj0);
+ assertEquals(1,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ pool.invalidateObject(keya,obj1);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ pool.close();
+ }
+
+ @Test
+ public void testBaseNumActiveNumIdle() {
+ try {
+ pool = makeEmptyPool(3);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object keya = makeKey(0);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ final Object obj0 = pool.borrowObject(keya);
+ assertEquals(1,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ final Object obj1 = pool.borrowObject(keya);
+ assertEquals(2,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ pool.returnObject(keya,obj1);
+ assertEquals(1,pool.getNumActive(keya));
+ assertEquals(1,pool.getNumIdle(keya));
+ pool.returnObject(keya,obj0);
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(2,pool.getNumIdle(keya));
+
+ assertEquals(0,pool.getNumActive("xyzzy12345"));
+ assertEquals(0,pool.getNumIdle("xyzzy12345"));
+
+ pool.close();
+ }
+
+ @Test
+ public void testBaseNumActiveNumIdle2() {
+ try {
+ pool = makeEmptyPool(6);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // skip this test if unsupported
+ }
+ final Object keya = makeKey(0);
+ final Object keyb = makeKey(1);
+ assertEquals(0,pool.getNumActive());
+ assertEquals(0,pool.getNumIdle());
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ assertEquals(0,pool.getNumActive(keyb));
+ assertEquals(0,pool.getNumIdle(keyb));
+
+ final Object objA0 = pool.borrowObject(keya);
+ final Object objB0 = pool.borrowObject(keyb);
+
+ assertEquals(2,pool.getNumActive());
+ assertEquals(0,pool.getNumIdle());
+ assertEquals(1,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ assertEquals(1,pool.getNumActive(keyb));
+ assertEquals(0,pool.getNumIdle(keyb));
+
+ final Object objA1 = pool.borrowObject(keya);
+ final Object objB1 = pool.borrowObject(keyb);
+
+ assertEquals(4,pool.getNumActive());
+ assertEquals(0,pool.getNumIdle());
+ assertEquals(2,pool.getNumActive(keya));
+ assertEquals(0,pool.getNumIdle(keya));
+ assertEquals(2,pool.getNumActive(keyb));
+ assertEquals(0,pool.getNumIdle(keyb));
+
+ pool.returnObject(keya,objA0);
+ pool.returnObject(keyb,objB0);
+
+ assertEquals(2,pool.getNumActive());
+ assertEquals(2,pool.getNumIdle());
+ assertEquals(1,pool.getNumActive(keya));
+ assertEquals(1,pool.getNumIdle(keya));
+ assertEquals(1,pool.getNumActive(keyb));
+ assertEquals(1,pool.getNumIdle(keyb));
+
+ pool.returnObject(keya,objA1);
+ pool.returnObject(keyb,objB1);
+
+ assertEquals(0,pool.getNumActive());
+ assertEquals(4,pool.getNumIdle());
+ assertEquals(0,pool.getNumActive(keya));
+ assertEquals(2,pool.getNumIdle(keya));
+ assertEquals(0,pool.getNumActive(keyb));
+ assertEquals(2,pool.getNumIdle(keyb));
+
+ pool.close();
+ }
+
+ @Test
+ public void testClosedPoolBehavior() {
+ final KeyedObjectPool<Object, Object, RuntimeException> pool;
+ try {
+ pool = makeEmptyPool(new TestFactory());
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+
+ final Object o1 = pool.borrowObject(KEY);
+ final Object o2 = pool.borrowObject(KEY);
+
+ pool.close();
+
+ assertThrows(IllegalStateException.class, () -> pool.addObject(KEY),
+ "A closed pool must throw an IllegalStateException when addObject is called.");
+
+ assertThrows(IllegalStateException.class, () -> pool.borrowObject(KEY),
+ "A closed pool must throw an IllegalStateException when borrowObject is called.");
+
+ // The following should not throw exceptions just because the pool is closed.
+ assertEquals( 0, pool.getNumIdle(KEY),"A closed pool shouldn't have any idle objects.");
+ assertEquals( 0, pool.getNumIdle(),"A closed pool shouldn't have any idle objects.");
+ pool.getNumActive();
+ pool.getNumActive(KEY);
+ pool.returnObject(KEY, o1);
+ assertEquals( 0, pool.getNumIdle(KEY),"returnObject should not add items back into the idle object pool for a closed pool.");
+ assertEquals( 0, pool.getNumIdle(),"returnObject should not add items back into the idle object pool for a closed pool.");
+ pool.invalidateObject(KEY, o2);
+ pool.clear(KEY);
+ pool.clear();
+ pool.close();
+ }
+
+ @Test
+ public void testKPOFAddObjectUsage() {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object, Object, PrivateException> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+
+ // addObject should make a new object, passivate it and put it in the pool
+ pool.addObject(KEY);
+ expectedMethods.add(new MethodCall("makeObject", KEY).returned(ZERO));
+ expectedMethods.add(new MethodCall("passivateObject", KEY, ZERO));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+
+ // Test exception handling of addObject
+ reset(pool, factory, expectedMethods);
+
+ // makeObject Exceptions should be propagated to client code from addObject
+ factory.setMakeObjectFail(true);
+ assertThrows(PrivateException.class, () -> pool.addObject(KEY), "Expected addObject to propagate makeObject exception.");
+ expectedMethods.add(new MethodCall("makeObject", KEY));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+
+ clear(factory, expectedMethods);
+
+ // passivateObject Exceptions should be propagated to client code from addObject
+ factory.setMakeObjectFail(false);
+ factory.setPassivateObjectFail(true);
+ assertThrows(PrivateException.class, () -> pool.addObject(KEY), "Expected addObject to propagate passivateObject exception.");
+ expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+ expectedMethods.add(new MethodCall("passivateObject", KEY, ONE));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.close();
+ }
+
+ @Test
+ public void testKPOFBorrowObjectUsages() {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object, Object, PrivateException> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
+
+ if (pool instanceof GenericKeyedObjectPool) {
+ ((GenericKeyedObjectPool<Object, Object, PrivateException>) pool).setTestOnBorrow(true);
+ }
+
+ // Test correct behavior code paths
+
+ // existing idle object should be activated and validated
+ pool.addObject(KEY);
+ clear(factory, expectedMethods);
+ obj = pool.borrowObject(KEY);
+ expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
+ expectedMethods.add(new MethodCall("validateObject", KEY, ZERO).returned(Boolean.TRUE));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.returnObject(KEY, obj);
+
+ // Test exception handling of borrowObject
+ reset(pool, factory, expectedMethods);
+
+ // makeObject Exceptions should be propagated to client code from borrowObject
+ factory.setMakeObjectFail(true);
+ try {
+ obj = pool.borrowObject(KEY);
+ fail("Expected borrowObject to propagate makeObject exception.");
+ } catch (final PrivateException pe) {
+ // expected
+ }
+ expectedMethods.add(new MethodCall("makeObject", KEY));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+
+
+ // when activateObject fails in borrowObject, a new object should be borrowed/created
+ reset(pool, factory, expectedMethods);
+ pool.addObject(KEY);
+ clear(factory, expectedMethods);
+
+ factory.setActivateObjectFail(true);
+ expectedMethods.add(new MethodCall("activateObject", KEY, obj));
+ assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
+ // After idle object fails validation, new on is created and activation
+ // fails again for the new one.
+ expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+ expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
+ TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+ assertEquals(expectedMethods, factory.getMethodCalls());
+
+ // when validateObject fails in borrowObject, a new object should be borrowed/created
+ reset(pool, factory, expectedMethods);
+ pool.addObject(KEY);
+ clear(factory, expectedMethods);
+
+ factory.setValidateObjectFail(true);
+ // testOnBorrow is on, so this will throw when the newly created instance
+ // fails validation
+ assertThrows(NoSuchElementException.class, () -> pool.borrowObject(KEY));
+ // Activate, then validate for idle instance
+ expectedMethods.add(new MethodCall("activateObject", KEY, ZERO));
+ expectedMethods.add(new MethodCall("validateObject", KEY, ZERO));
+ // Make new instance, activate succeeds, validate fails
+ expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+ expectedMethods.add(new MethodCall("activateObject", KEY, ONE));
+ expectedMethods.add(new MethodCall("validateObject", KEY, ONE));
+ TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.close();
+ }
+
+ @Test
+ public void testKPOFClearUsages() {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object, Object, PrivateException> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+
+ // Test correct behavior code paths
+ pool.addObjects(KEY, 5);
+ pool.clear();
+
+ // Test exception handling clear should swallow destroy object failures
+ reset(pool, factory, expectedMethods);
+ factory.setDestroyObjectFail(true);
+ pool.addObjects(KEY, 5);
+ pool.clear();
+ pool.close();
+ }
+
+
+ @Test
+ public void testKPOFCloseUsages() {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ KeyedObjectPool<Object, Object, PrivateException> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch (final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+
+ // Test correct behavior code paths
+ pool.addObjects(KEY, 5);
+ pool.close();
+
+ // Test exception handling close should swallow failures
+ try (final KeyedObjectPool<Object, Object, PrivateException> pool2 = makeEmptyPool(factory)) {
+ reset(pool2, factory, expectedMethods);
+ factory.setDestroyObjectFail(true);
+ pool2.addObjects(KEY, 5);
+ }
+ }
+
+ @Test
+ public void testKPOFInvalidateObjectUsages() throws InterruptedException {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object, Object, PrivateException> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
+
+ // Test correct behavior code paths
+
+ obj = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
+
+ // invalidated object should be destroyed
+ pool.invalidateObject(KEY, obj);
+ expectedMethods.add(new MethodCall("destroyObject", KEY, obj));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+
+ // Test exception handling of invalidateObject
+ reset(pool, factory, expectedMethods);
+ final Object obj2 = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
+ factory.setDestroyObjectFail(true);
+ assertThrows(PrivateException.class, () -> pool.invalidateObject(KEY, obj2), "Expecting destroy exception to propagate");
+ Thread.sleep(250); // could be defered
+ TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.close();
+ }
+
+ @Test
+ public void testKPOFReturnObjectUsages() {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object, Object, PrivateException> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
+
+ // Test correct behavior code paths
+ obj = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
+
+ // returned object should be passivated
+ pool.returnObject(KEY, obj);
+ expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+
+ // Test exception handling of returnObject
+ reset(pool, factory, expectedMethods);
+
+ // passivateObject should swallow exceptions and not add the object to the pool
+ pool.addObject(KEY);
+ pool.addObject(KEY);
+ pool.addObject(KEY);
+ assertEquals(3, pool.getNumIdle(KEY));
+ obj = pool.borrowObject(KEY);
+ obj = pool.borrowObject(KEY);
+ assertEquals(1, pool.getNumIdle(KEY));
+ assertEquals(2, pool.getNumActive(KEY));
+ clear(factory, expectedMethods);
+ factory.setPassivateObjectFail(true);
+ pool.returnObject(KEY, obj);
+ expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
+ TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ assertEquals(1, pool.getNumIdle(KEY)); // Not added
+ assertEquals(1, pool.getNumActive(KEY)); // But not active
+
+ reset(pool, factory, expectedMethods);
+ obj = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
+ factory.setPassivateObjectFail(true);
+ factory.setDestroyObjectFail(true);
+ try {
+ pool.returnObject(KEY, obj);
+ if (!(pool instanceof GenericKeyedObjectPool)) { // ugh, 1.3-compat
+ fail("Expecting destroyObject exception to be propagated");
+ }
+ } catch (final PrivateException ex) {
+ // Expected
+ }
+ pool.close();
+ }
+
+ @Test
+ public void testToString() {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ try (final KeyedObjectPool<Object, Object, PrivateException> pool = makeEmptyPool(factory)) {
+ pool.toString();
+ } catch (final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
+ }
+}
diff --git a/src/test/java/org/apache/commons/pool2/WaiterFactory.java b/src/test/java/org/apache/commons/pool2/WaiterFactory.java
index 3a190391..014bf375 100644
--- a/src/test/java/org/apache/commons/pool2/WaiterFactory.java
+++ b/src/test/java/org/apache/commons/pool2/WaiterFactory.java
@@ -1,217 +1,217 @@
-/*
- * 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;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.commons.pool2.impl.DefaultPooledObject;
-
-/**
- * Object factory with configurable latencies for object lifecycle methods.
- * This factory will also track and enforce maxActive, maxActivePerKey contracts.
- * If the factory's maxActive / maxActivePerKey are set to match those of the
- * pool, makeObject will throw IllegalStateException if the number of makes - destroys
- * (per key) exceeds the configured max.
- *
- * @param <K> The type of keys managed by this factory.
- */
-public class WaiterFactory<K> implements PooledObjectFactory<Waiter, IllegalStateException>, KeyedPooledObjectFactory<K, Waiter, RuntimeException> {
-
- /** Latency of activateObject */
- private final long activateLatency;
-
- /** Latency of destroyObject */
- private final long destroyLatency;
-
- /** Latency of makeObject */
- private final long makeLatency;
-
- /** Latency of passivateObject */
- private final long passivateLatency;
-
- /** Latency of validateObject */
- private final long validateLatency;
-
- /** Latency of doWait for Waiter instances created by this factory */
- private final long waiterLatency;
-
- /** Probability that passivation will invalidate Waiter instances */
- private final double passivateInvalidationProbability;
-
- /** Count of (makes - destroys) since last reset */
- private long activeCount;
-
- /** Count of (makes - destroys) per key since last reset */
- private final Map<K,Integer> activeCounts = new HashMap<>();
-
- /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
- private final long maxActive; // GKOP 1.x calls this maxTotal
-
- /** Maximum of (makes - destroys) per key */
- private final long maxActivePerKey; // GKOP 1.x calls this maxActive
-
- public WaiterFactory(final long activateLatency, final long destroyLatency,
- final long makeLatency, final long passivateLatency, final long validateLatency,
- final long waiterLatency) {
- this(activateLatency, destroyLatency, makeLatency, passivateLatency,
- validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
- }
-
- public WaiterFactory(final long activateLatency, final long destroyLatency,
- final long makeLatency, final long passivateLatency, final long validateLatency,
- final long waiterLatency,final long maxActive) {
- this(activateLatency, destroyLatency, makeLatency, passivateLatency,
- validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
- }
-
- public WaiterFactory(final long activateLatency, final long destroyLatency,
- final long makeLatency, final long passivateLatency, final long validateLatency,
- final long waiterLatency,final long maxActive, final long maxActivePerKey,
- final double passivateInvalidationProbability) {
- this.activateLatency = activateLatency;
- this.destroyLatency = destroyLatency;
- this.makeLatency = makeLatency;
- this.passivateLatency = passivateLatency;
- this.validateLatency = validateLatency;
- this.waiterLatency = waiterLatency;
- this.maxActive = maxActive;
- this.maxActivePerKey = maxActivePerKey;
- this.passivateInvalidationProbability = passivateInvalidationProbability;
- }
-
- @Override
- public void activateObject(final K key, final PooledObject<Waiter> obj) {
- activateObject(obj);
- }
-
- @Override
- public void activateObject(final PooledObject<Waiter> obj) {
- doWait(activateLatency);
- obj.getObject().setActive(true);
- }
-
- @Override
- public void destroyObject(final K key,final PooledObject<Waiter> obj) {
- destroyObject(obj);
- synchronized (this) {
- final Integer count = activeCounts.get(key);
- activeCounts.put(key, Integer.valueOf(count.intValue() - 1));
- }
- }
-
- @Override
- public void destroyObject(final PooledObject<Waiter> obj) {
- doWait(destroyLatency);
- obj.getObject().setValid(false);
- obj.getObject().setActive(false);
- // Decrement *after* destroy
- synchronized (this) {
- activeCount--;
- }
- }
-
- protected void doWait(final long latency) {
- if (latency == 0) {
- return;
- }
- Waiter.sleepQuietly(latency);
- }
-
- /**
- * @return the maxActive
- */
- public synchronized long getMaxActive() {
- return maxActive;
- }
-
- @Override
- public PooledObject<Waiter> makeObject() {
- // Increment and test *before* make
- synchronized (this) {
- if (activeCount >= maxActive) {
- throw new IllegalStateException("Too many active instances: " +
- activeCount + " in circulation with maxActive = " + maxActive);
- }
- activeCount++;
- }
- doWait(makeLatency);
- return new DefaultPooledObject<>(new Waiter(false, true, waiterLatency));
- }
-
- @Override
- public PooledObject<Waiter> makeObject(final K key) {
- synchronized (this) {
- Integer count = activeCounts.get(key);
- if (count == null) {
- count = Integer.valueOf(1);
- activeCounts.put(key, count);
- } else {
- if (count.intValue() >= maxActivePerKey) {
- throw new IllegalStateException("Too many active " +
- "instances for key = " + key + ": " + count.intValue() +
- " in circulation " + "with maxActivePerKey = " +
- maxActivePerKey);
- }
- activeCounts.put(key, Integer.valueOf(count.intValue() + 1));
- }
- }
- return makeObject();
- }
-
- // KeyedPoolableObjectFactory methods
-
- @Override
- public void passivateObject(final K key, final PooledObject<Waiter> obj) {
- passivateObject(obj);
- }
-
- @Override
- public void passivateObject(final PooledObject<Waiter> obj) {
- obj.getObject().setActive(false);
- doWait(passivateLatency);
- if (Math.random() < passivateInvalidationProbability) {
- obj.getObject().setValid(false);
- }
- }
-
- public synchronized void reset() {
- activeCount = 0;
- if (activeCounts.isEmpty()) {
- return;
- }
- final Iterator<K> it = activeCounts.keySet().iterator();
- while (it.hasNext()) {
- final K key = it.next();
- activeCounts.put(key, Integer.valueOf(0));
- }
- }
-
- @Override
- public boolean validateObject(final K key, final PooledObject<Waiter> obj) {
- return validateObject(obj);
- }
-
- @Override
- public boolean validateObject(final PooledObject<Waiter> obj) {
- doWait(validateLatency);
- return obj.getObject().isValid();
- }
-
-}
+/*
+ * 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;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+/**
+ * Object factory with configurable latencies for object lifecycle methods.
+ * This factory will also track and enforce maxActive, maxActivePerKey contracts.
+ * If the factory's maxActive / maxActivePerKey are set to match those of the
+ * pool, makeObject will throw IllegalStateException if the number of makes - destroys
+ * (per key) exceeds the configured max.
+ *
+ * @param <K> The type of keys managed by this factory.
+ */
+public class WaiterFactory<K> implements PooledObjectFactory<Waiter, IllegalStateException>, KeyedPooledObjectFactory<K, Waiter, RuntimeException> {
+
+ /** Latency of activateObject */
+ private final long activateLatency;
+
+ /** Latency of destroyObject */
+ private final long destroyLatency;
+
+ /** Latency of makeObject */
+ private final long makeLatency;
+
+ /** Latency of passivateObject */
+ private final long passivateLatency;
+
+ /** Latency of validateObject */
+ private final long validateLatency;
+
+ /** Latency of doWait for Waiter instances created by this factory */
+ private final long waiterLatency;
+
+ /** Probability that passivation will invalidate Waiter instances */
+ private final double passivateInvalidationProbability;
+
+ /** Count of (makes - destroys) since last reset */
+ private long activeCount;
+
+ /** Count of (makes - destroys) per key since last reset */
+ private final Map<K,Integer> activeCounts = new HashMap<>();
+
+ /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
+ private final long maxActive; // GKOP 1.x calls this maxTotal
+
+ /** Maximum of (makes - destroys) per key */
+ private final long maxActivePerKey; // GKOP 1.x calls this maxActive
+
+ public WaiterFactory(final long activateLatency, final long destroyLatency,
+ final long makeLatency, final long passivateLatency, final long validateLatency,
+ final long waiterLatency) {
+ this(activateLatency, destroyLatency, makeLatency, passivateLatency,
+ validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
+ }
+
+ public WaiterFactory(final long activateLatency, final long destroyLatency,
+ final long makeLatency, final long passivateLatency, final long validateLatency,
+ final long waiterLatency,final long maxActive) {
+ this(activateLatency, destroyLatency, makeLatency, passivateLatency,
+ validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
+ }
+
+ public WaiterFactory(final long activateLatency, final long destroyLatency,
+ final long makeLatency, final long passivateLatency, final long validateLatency,
+ final long waiterLatency,final long maxActive, final long maxActivePerKey,
+ final double passivateInvalidationProbability) {
+ this.activateLatency = activateLatency;
+ this.destroyLatency = destroyLatency;
+ this.makeLatency = makeLatency;
+ this.passivateLatency = passivateLatency;
+ this.validateLatency = validateLatency;
+ this.waiterLatency = waiterLatency;
+ this.maxActive = maxActive;
+ this.maxActivePerKey = maxActivePerKey;
+ this.passivateInvalidationProbability = passivateInvalidationProbability;
+ }
+
+ @Override
+ public void activateObject(final K key, final PooledObject<Waiter> obj) {
+ activateObject(obj);
+ }
+
+ @Override
+ public void activateObject(final PooledObject<Waiter> obj) {
+ doWait(activateLatency);
+ obj.getObject().setActive(true);
+ }
+
+ @Override
+ public void destroyObject(final K key,final PooledObject<Waiter> obj) {
+ destroyObject(obj);
+ synchronized (this) {
+ final Integer count = activeCounts.get(key);
+ activeCounts.put(key, Integer.valueOf(count.intValue() - 1));
+ }
+ }
+
+ @Override
+ public void destroyObject(final PooledObject<Waiter> obj) {
+ doWait(destroyLatency);
+ obj.getObject().setValid(false);
+ obj.getObject().setActive(false);
+ // Decrement *after* destroy
+ synchronized (this) {
+ activeCount--;
+ }
+ }
+
+ protected void doWait(final long latency) {
+ if (latency == 0) {
+ return;
+ }
+ Waiter.sleepQuietly(latency);
+ }
+
+ /**
+ * @return the maxActive
+ */
+ public synchronized long getMaxActive() {
+ return maxActive;
+ }
+
+ @Override
+ public PooledObject<Waiter> makeObject() {
+ // Increment and test *before* make
+ synchronized (this) {
+ if (activeCount >= maxActive) {
+ throw new IllegalStateException("Too many active instances: " +
+ activeCount + " in circulation with maxActive = " + maxActive);
+ }
+ activeCount++;
+ }
+ doWait(makeLatency);
+ return new DefaultPooledObject<>(new Waiter(false, true, waiterLatency));
+ }
+
+ @Override
+ public PooledObject<Waiter> makeObject(final K key) {
+ synchronized (this) {
+ Integer count = activeCounts.get(key);
+ if (count == null) {
+ count = Integer.valueOf(1);
+ activeCounts.put(key, count);
+ } else {
+ if (count.intValue() >= maxActivePerKey) {
+ throw new IllegalStateException("Too many active " +
+ "instances for key = " + key + ": " + count.intValue() +
+ " in circulation " + "with maxActivePerKey = " +
+ maxActivePerKey);
+ }
+ activeCounts.put(key, Integer.valueOf(count.intValue() + 1));
+ }
+ }
+ return makeObject();
+ }
+
+ // KeyedPoolableObjectFactory methods
+
+ @Override
+ public void passivateObject(final K key, final PooledObject<Waiter> obj) {
+ passivateObject(obj);
+ }
+
+ @Override
+ public void passivateObject(final PooledObject<Waiter> obj) {
+ obj.getObject().setActive(false);
+ doWait(passivateLatency);
+ if (Math.random() < passivateInvalidationProbability) {
+ obj.getObject().setValid(false);
+ }
+ }
+
+ public synchronized void reset() {
+ activeCount = 0;
+ if (activeCounts.isEmpty()) {
+ return;
+ }
+ final Iterator<K> it = activeCounts.keySet().iterator();
+ while (it.hasNext()) {
+ final K key = it.next();
+ activeCounts.put(key, Integer.valueOf(0));
+ }
+ }
+
+ @Override
+ public boolean validateObject(final K key, final PooledObject<Waiter> obj) {
+ return validateObject(obj);
+ }
+
+ @Override
+ public boolean validateObject(final PooledObject<Waiter> obj) {
+ doWait(validateLatency);
+ return obj.getObject().isValid();
+ }
+
+}
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 071cabb1..548626b5 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
@@ -126,11 +126,11 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
this.pool = pool;
this.key = key;
}
-
+
public boolean complete() {
return done;
}
-
+
@Override
public void run() {
try {
@@ -226,55 +226,55 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
}
return new DefaultPooledObject<>(out);
}
-
+
@Override
public void passivateObject(final K key, final PooledObject<String> obj) throws TestException {
if (exceptionOnPassivate) {
throw new TestException();
}
}
-
+
public void setDestroyLatency(final long destroyLatency) {
this.destroyLatency = destroyLatency;
}
-
+
void setEvenValid(final boolean valid) {
evenValid = valid;
}
-
+
public void setMakeLatency(final long makeLatency) {
this.makeLatency = makeLatency;
}
-
+
public void setMaxTotalPerKey(final int maxTotalPerKey) {
this.maxTotalPerKey = maxTotalPerKey;
}
-
+
public void setThrowExceptionOnActivate(final boolean b) {
exceptionOnActivate = b;
}
-
+
public void setThrowExceptionOnDestroy(final boolean b) {
exceptionOnDestroy = b;
}
-
+
public void setThrowExceptionOnPassivate(final boolean b) {
exceptionOnPassivate = b;
}
-
+
public void setThrowExceptionOnValidate(final boolean b) {
exceptionOnValidate = b;
}
-
+
void setValid(final boolean valid) {
evenValid = valid;
oddValid = valid;
}
-
+
public void setValidateLatency(final long validateLatency) {
this.validateLatency = validateLatency;
}
-
+
public void setValidationEnabled(final boolean b) {
enableValidation = b;
}
@@ -291,7 +291,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
return valid;
}
}
-
+
private static class SimplePerKeyFactory extends BaseKeyedPooledObjectFactory<Object, Object, RuntimeException> {
final ConcurrentHashMap<Object, AtomicInteger> map = new ConcurrentHashMap<>();
@@ -496,7 +496,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
private static final Integer KEY_TWO = Integer.valueOf(2);
private static final boolean DISPLAY_THREAD_DETAILS=
- Boolean.parseBoolean(System.getProperty("TestGenericKeyedObjectPool.display.thread.details", "false"));
+ Boolean.getBoolean("TestGenericKeyedObjectPool.display.thread.details");
// To pass this to a Maven test, use:
// mvn test -DargLine="-DTestGenericKeyedObjectPool.display.thread.details=true"
// @see https://issues.apache.org/jira/browse/SUREFIRE-121
@@ -684,7 +684,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
// FIFO - 24, 25, 26
for (int i = 0; i < 8; i++) {
final VisitTracker<Integer> tracker = intPool.borrowObject(KEY_ONE);
- if ((lifo && tracker.getId() > 1) || (!lifo && tracker.getId() > 2)) {
+ if (lifo && tracker.getId() > 1 || !lifo && tracker.getId() > 2) {
assertEquals( 1,
tracker.getValidateCount(),"Instance " + tracker.getId() + " visited wrong number of times.");
} else {
@@ -738,7 +738,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
final int totalInstances = zeroLength + oneLength + twoLength;
// Number of times evictor should have cycled through pools
- final int cycleCount = (runs * intPool.getNumTestsPerEvictionRun()) / totalInstances;
+ final int cycleCount = runs * intPool.getNumTestsPerEvictionRun() / totalInstances;
// Look at elements and make sure they are visited cycleCount
// or cycleCount + 1 times
@@ -861,7 +861,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
t.start();
}
for (final TestThread<T, E> testThread : threads) {
- while (!(testThread.complete())) {
+ while (!testThread.complete()) {
Waiter.sleepQuietly(500L);
}
if (testThread.failed()) {
@@ -909,12 +909,12 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
@Test
public void testAppendStats() {
assertFalse(gkoPool.getMessageStatistics());
- assertEquals("foo", (gkoPool.appendStats("foo")));
+ assertEquals("foo", gkoPool.appendStats("foo"));
try (final GenericKeyedObjectPool<?, ?, TestException> pool = new GenericKeyedObjectPool<>(new SimpleFactory<>())) {
pool.setMessagesStatistics(true);
- assertNotEquals("foo", (pool.appendStats("foo")));
+ assertNotEquals("foo", pool.appendStats("foo"));
pool.setMessagesStatistics(false);
- assertEquals("foo", (pool.appendStats("foo")));
+ assertEquals("foo", pool.appendStats("foo"));
}
}
@@ -929,7 +929,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
final long startMillis = System.currentTimeMillis();
// Needs to be in a separate thread as this will block
final Runnable simple = new SimpleTestThread<>(gkoPool, "one");
- (new Thread(simple)).start();
+ new Thread(simple).start();
// This should be almost instant. If it isn't it means this thread got
// stuck behind the thread created above which is bad.
// Give other thread a chance to start
@@ -940,7 +940,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
// If it passes it should be almost instant
// Use 3000ms as the threshold - should avoid timing issues on most
// (all? platforms)
- assertTrue((endMillis - startMillis) < 4000,
+ assertTrue(endMillis - startMillis < 4000,
"Elapsed time: " + (endMillis - startMillis) + " should be less than 4000");
}
@@ -995,7 +995,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
// Wait for threads to finish
for (int i = 0; i < numThreads; i++) {
- while (!(threads[i]).complete()) {
+ while (!threads[i].complete()) {
Waiter.sleepQuietly(500L);
}
if (threads[i].failed()) {
@@ -1099,7 +1099,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
final String six2 = gkoPool.borrowObject("six");
Thread.sleep(100);
// Launch the waiters - all will be blocked waiting
- for (Thread t : testThreads) {
+ for (final Thread t : testThreads) {
t.start();
}
Thread.sleep(100);
@@ -1120,7 +1120,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
gkoPool.returnObject("six", six2);
gkoPool.clear("six");
Thread.sleep(20);
- for (Thread t : testThreads) {
+ for (final Thread t : testThreads) {
assertFalse(t.isAlive());
}
}
@@ -1139,52 +1139,52 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
config.setMaxIdlePerKey(-1);
config.setMaxTotal(-1);
config.setMaxWait(Duration.ofMillis(5));
- GenericKeyedObjectPool<Integer, Integer, InterruptedException> testPool = new GenericKeyedObjectPool<>(
+ final GenericKeyedObjectPool<Integer, Integer, InterruptedException> testPool = new GenericKeyedObjectPool<>(
new KeyedPooledObjectFactory<Integer, Integer, InterruptedException>() {
@Override
- public void activateObject(Integer key, PooledObject<Integer> p) {
+ public void activateObject(final Integer key, final PooledObject<Integer> p) {
// do nothing
}
@Override
- public void destroyObject(Integer key, PooledObject<Integer> p) throws InterruptedException {
+ public void destroyObject(final Integer key, final PooledObject<Integer> p) throws InterruptedException {
Thread.sleep(500);
}
@Override
- public PooledObject<Integer> makeObject(Integer key) {
+ public PooledObject<Integer> makeObject(final Integer key) {
return new DefaultPooledObject<>(10);
}
@Override
- public void passivateObject(Integer key, PooledObject<Integer> p) {
+ public void passivateObject(final Integer key, final PooledObject<Integer> p) {
// do nothing
}
@Override
- public boolean validateObject(Integer key, PooledObject<Integer> p) {
+ public boolean validateObject(final Integer key, final PooledObject<Integer> p) {
return true;
}
}, config);
final int borrowKey = 10;
- Thread t = new Thread(() -> {
+ final Thread t = new Thread(() -> {
try {
while (true) {
- Integer integer = testPool.borrowObject(borrowKey);
+ final Integer integer = testPool.borrowObject(borrowKey);
testPool.returnObject(borrowKey, integer);
Thread.sleep(10);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
fail();
}
});
- Thread t2 = new Thread(() -> {
+ final Thread t2 = new Thread(() -> {
try {
while (true) {
testPool.clear(borrowKey);
Thread.sleep(10);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
fail();
}
});
@@ -1683,7 +1683,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
@Test
public void testGetStatsString() {
- assertNotNull((gkoPool.getStatsString()));
+ assertNotNull(gkoPool.getStatsString());
}
/**
@@ -1722,8 +1722,8 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
public void testInvalidateFreesCapacityForOtherKeys() throws Exception {
gkoPool.setMaxTotal(1);
gkoPool.setMaxWait(Duration.ofMillis(500));
- Thread borrower = new Thread(new SimpleTestThread<>(gkoPool, "two"));
- String obj = gkoPool.borrowObject("one");
+ final Thread borrower = new Thread(new SimpleTestThread<>(gkoPool, "two"));
+ final String obj = gkoPool.borrowObject("one");
borrower.start(); // Will block
Thread.sleep(100); // Make sure borrower has started
gkoPool.invalidateObject("one", obj); // Should free capacity to serve the other key
@@ -1915,7 +1915,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
for(int i=0;i<100;i++) {
gkoPool.returnObject("",active[i]);
assertEquals(99 - i,gkoPool.getNumActive(""));
- assertEquals((i < 8 ? i+1 : 8),gkoPool.getNumIdle(""));
+ assertEquals(i < 8 ? i+1 : 8,gkoPool.getNumIdle(""));
}
for(int i=0;i<100;i++) {
@@ -1926,7 +1926,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
for(int i=0;i<100;i++) {
gkoPool.returnObject("a",active[i]);
assertEquals(99 - i,gkoPool.getNumActive("a"));
- assertEquals((i < 8 ? i+1 : 8),gkoPool.getNumIdle("a"));
+ assertEquals(i < 8 ? i+1 : 8,gkoPool.getNumIdle("a"));
}
// total number of idle instances is twice maxIdle
@@ -2169,7 +2169,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
" BorrowTime: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) +
" PostReturn: " + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) +
" Ended: " + (wt.endedMillis - originMillis) +
- " Key: " + (wt.key) +
+ " Key: " + wt.key +
" ObjId: " + wt.objectId
);
}
@@ -2418,7 +2418,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
gkoPool.setMaxTotal(1);
// Test return object with no take waiters
- String obj = gkoPool.borrowObject("0");
+ final String obj = gkoPool.borrowObject("0");
gkoPool.returnObject("0", obj);
// Test return object with a take waiter
@@ -2437,7 +2437,7 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
gkoPool.setBlockWhenExhausted(false);
// Test return object with no take waiters
- String obj = gkoPool.borrowObject("0");
+ final String obj = gkoPool.borrowObject("0");
gkoPool.returnObject("0", obj);
}
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 f7cae2bf..7ae0a386 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
@@ -1,3011 +1,3011 @@
-/*
- * 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.impl;
-
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.lessThanOrEqualTo;
-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;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.lang.management.ManagementFactory;
-import java.lang.ref.WeakReference;
-import java.nio.charset.UnsupportedCharsetException;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
-import org.apache.commons.pool2.BasePooledObjectFactory;
-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.SwallowedExceptionListener;
-import org.apache.commons.pool2.TestBaseObjectPool;
-import org.apache.commons.pool2.TestException;
-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.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Timeout;
-
-/**
- */
-public class TestGenericObjectPool extends TestBaseObjectPool {
-
- private class ConcurrentBorrowAndEvictThread extends Thread {
- private final boolean borrow;
- public String obj;
-
- public ConcurrentBorrowAndEvictThread(final boolean borrow) {
- this.borrow = borrow;
- }
-
- @Override
- public void run() {
- try {
- if (borrow) {
- obj = genericObjectPool.borrowObject();
- } else {
- genericObjectPool.evict();
- }
- } catch (final Exception e) {
- // Ignore.
- }
- }
- }
-
- private static class CreateErrorFactory extends BasePooledObjectFactory<String, InterruptedException> {
-
- private final Semaphore semaphore = new Semaphore(0);
-
- @Override
- public String create() throws InterruptedException {
- semaphore.acquire();
- throw new UnknownError("wiggle");
- }
-
- public boolean hasQueuedThreads() {
- return semaphore.hasQueuedThreads();
- }
-
- public void release() {
- semaphore.release();
- }
-
- @Override
- public PooledObject<String> wrap(final String obj) {
- return new DefaultPooledObject<>(obj);
- }
- }
-
- private static class CreateFailFactory extends BasePooledObjectFactory<String, InterruptedException> {
-
- private final Semaphore semaphore = new Semaphore(0);
-
- @Override
- public String create() throws InterruptedException {
- semaphore.acquire();
- throw new UnsupportedCharsetException("wibble");
- }
-
- public boolean hasQueuedThreads() {
- return semaphore.hasQueuedThreads();
- }
-
- public void release() {
- semaphore.release();
- }
-
- @Override
- public PooledObject<String> wrap(final String obj) {
- return new DefaultPooledObject<>(obj);
- }
- }
-
- private static final class DummyFactory
- extends BasePooledObjectFactory<Object, RuntimeException> {
- @Override
- public Object create() {
- return null;
- }
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
- }
- }
-
- private static class EvictionThread<T, E extends Exception> extends Thread {
-
- private final GenericObjectPool<T, E> pool;
-
- public EvictionThread(final GenericObjectPool<T, E> pool) {
- this.pool = pool;
- }
-
- @Override
- public void run() {
- try {
- pool.evict();
- } catch (final Exception e) {
- // Ignore
- }
- }
- }
-
- /**
- * Factory that creates HashSets. Note that this means
- * 0) All instances are initially equal (not discernible by equals)
- * 1) Instances are mutable and mutation can cause change in identity / hashcode.
- */
- private static final class HashSetFactory
- extends BasePooledObjectFactory<HashSet<String>, RuntimeException> {
- @Override
- public HashSet<String> create() {
- return new HashSet<>();
- }
- @Override
- public PooledObject<HashSet<String>> wrap(final HashSet<String> value) {
- return new DefaultPooledObject<>(value);
- }
- }
-
- /**
- * Attempts to invalidate an object, swallowing IllegalStateException.
- */
- static class InvalidateThread implements Runnable {
- private final String obj;
- private final ObjectPool<String, ? extends Exception> pool;
- private boolean done;
- public InvalidateThread(final ObjectPool<String, ? extends Exception> pool, final String obj) {
- this.obj = obj;
- this.pool = pool;
- }
- public boolean complete() {
- return done;
- }
- @Override
- public void run() {
- try {
- pool.invalidateObject(obj);
- } catch (final IllegalStateException ex) {
- // Ignore
- } catch (final Exception ex) {
- fail("Unexpected exception " + ex.toString());
- } finally {
- done = true;
- }
- }
- }
-
- private static class InvalidFactory
- extends BasePooledObjectFactory<Object, RuntimeException> {
-
- @Override
- public Object create() {
- return new Object();
- }
- @Override
- public boolean validateObject(final PooledObject<Object> obj) {
- Waiter.sleepQuietly(1000);
- return false;
- }
-
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
- }
- }
-
- public static class SimpleFactory implements PooledObjectFactory<String, TestException> {
- int makeCounter;
-
- int activationCounter;
-
- int validateCounter;
-
- int activeCount;
-
- boolean evenValid = true;
-
- boolean oddValid = true;
-
- boolean exceptionOnPassivate;
-
- boolean exceptionOnActivate;
-
- boolean exceptionOnDestroy;
-
- boolean exceptionOnValidate;
-
- boolean enableValidation = true;
-
- long destroyLatency;
-
- long makeLatency;
-
- long validateLatency;
-
- int maxTotal = Integer.MAX_VALUE;
-
- public SimpleFactory() {
- this(true);
- }
-
- public SimpleFactory(final boolean valid) {
- this(valid,valid);
- }
-
- public SimpleFactory(final boolean evalid, final boolean ovalid) {
- evenValid = evalid;
- oddValid = ovalid;
- }
-
- @Override
- public void activateObject(final PooledObject<String> obj) throws TestException {
- final boolean hurl;
- final boolean evenTest;
- final boolean oddTest;
- final int counter;
- synchronized(this) {
- hurl = exceptionOnActivate;
- evenTest = evenValid;
- oddTest = oddValid;
- counter = activationCounter++;
- }
- if (hurl && !(counter%2 == 0 ? evenTest : oddTest)) {
- throw new TestException();
- }
- }
-
- @Override
- public void destroyObject(final PooledObject<String> obj) throws TestException {
- final long waitLatency;
- final boolean hurl;
- synchronized(this) {
- waitLatency = destroyLatency;
- hurl = exceptionOnDestroy;
- }
- if (waitLatency > 0) {
- doWait(waitLatency);
- }
- synchronized(this) {
- activeCount--;
- }
- if (hurl) {
- throw new TestException();
- }
- }
-
- private void doWait(final long latency) {
- Waiter.sleepQuietly(latency);
- }
-
- public synchronized int getMakeCounter() {
- return makeCounter;
- }
-
- public synchronized boolean isThrowExceptionOnActivate() {
- return exceptionOnActivate;
- }
-
- public synchronized boolean isValidationEnabled() {
- return enableValidation;
- }
-
- @Override
- public PooledObject<String> makeObject() {
- final long waitLatency;
- synchronized(this) {
- activeCount++;
- if (activeCount > maxTotal) {
- throw new IllegalStateException(
- "Too many active instances: " + activeCount);
- }
- waitLatency = makeLatency;
- }
- if (waitLatency > 0) {
- doWait(waitLatency);
- }
- final int counter;
- synchronized(this) {
- counter = makeCounter++;
- }
- return new DefaultPooledObject<>(String.valueOf(counter));
- }
-
- @Override
- public void passivateObject(final PooledObject<String> obj) throws TestException {
- final boolean hurl;
- synchronized(this) {
- hurl = exceptionOnPassivate;
- }
- if (hurl) {
- throw new TestException();
- }
- }
-
- public synchronized void setDestroyLatency(final long destroyLatency) {
- this.destroyLatency = destroyLatency;
- }
-
- public synchronized void setEvenValid(final boolean valid) {
- evenValid = valid;
- }
-
- public synchronized void setMakeLatency(final long makeLatency) {
- this.makeLatency = makeLatency;
- }
-
- public synchronized void setMaxTotal(final int maxTotal) {
- this.maxTotal = maxTotal;
- }
-
- public synchronized void setOddValid(final boolean valid) {
- oddValid = valid;
- }
-
- public synchronized void setThrowExceptionOnActivate(final boolean b) {
- exceptionOnActivate = b;
- }
-
- public synchronized void setThrowExceptionOnDestroy(final boolean b) {
- exceptionOnDestroy = b;
- }
-
- public synchronized void setThrowExceptionOnPassivate(final boolean bool) {
- exceptionOnPassivate = bool;
- }
-
- public synchronized void setThrowExceptionOnValidate(final boolean bool) {
- exceptionOnValidate = bool;
- }
-
- public synchronized void setValid(final boolean valid) {
- setEvenValid(valid);
- setOddValid(valid);
- }
-
- public synchronized void setValidateLatency(final long validateLatency) {
- this.validateLatency = validateLatency;
- }
-
- public synchronized void setValidationEnabled(final boolean b) {
- enableValidation = b;
- }
-
- @Override
- public boolean validateObject(final PooledObject<String> obj) {
- final boolean validate;
- final boolean throwException;
- final boolean evenTest;
- final boolean oddTest;
- final long waitLatency;
- final int counter;
- synchronized(this) {
- validate = enableValidation;
- throwException = exceptionOnValidate;
- evenTest = evenValid;
- oddTest = oddValid;
- counter = validateCounter++;
- waitLatency = validateLatency;
- }
- if (waitLatency > 0) {
- doWait(waitLatency);
- }
- if (throwException) {
- throw new RuntimeException("validation failed");
- }
- if (validate) {
- return counter%2 == 0 ? evenTest : oddTest;
- }
- return true;
- }
- }
-
- public static class TestEvictionPolicy<T> implements EvictionPolicy<T> {
-
- private final AtomicInteger callCount = new AtomicInteger(0);
-
- @Override
- public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
- final int idleCount) {
- return callCount.incrementAndGet() > 1500;
- }
- }
-
- static class TestThread<T, E extends Exception> implements Runnable {
-
- /** source of random delay times */
- private final java.util.Random random;
-
- /** pool to borrow from */
- private final ObjectPool<T, E> pool;
-
- /** number of borrow attempts */
- private final int iter;
-
- /** delay before each borrow attempt */
- private final int startDelay;
-
- /** time to hold each borrowed object before returning it */
- private final int holdTime;
-
- /** whether or not start and hold time are randomly generated */
- private final boolean randomDelay;
-
- /** object expected to be borrowed (fail otherwise) */
- private final Object expectedObject;
-
- private volatile boolean complete;
- private volatile boolean failed;
- private volatile Throwable error;
-
- public TestThread(final ObjectPool<T, E> pool) {
- this(pool, 100, 50, true, null);
- }
-
- public TestThread(final ObjectPool<T, E> pool, final int iter) {
- this(pool, iter, 50, true, null);
- }
-
- public TestThread(final ObjectPool<T, E> pool, final int iter, final int delay) {
- this(pool, iter, delay, true, null);
- }
-
- public TestThread(final ObjectPool<T, E> pool, final int iter, final int delay,
- final boolean randomDelay) {
- this(pool, iter, delay, randomDelay, null);
- }
-
- public TestThread(final ObjectPool<T, E> pool, final int iter, final int delay,
- final boolean randomDelay, final Object obj) {
- this(pool, iter, delay, delay, randomDelay, obj);
- }
-
- public TestThread(final ObjectPool<T, E> pool, final int iter, final int startDelay,
- final int holdTime, final boolean randomDelay, final Object obj) {
- this.pool = pool;
- this.iter = iter;
- this.startDelay = startDelay;
- this.holdTime = holdTime;
- this.randomDelay = randomDelay;
- this.random = this.randomDelay ? new Random() : null;
- this.expectedObject = obj;
- }
-
- public boolean complete() {
- return complete;
- }
-
- public boolean failed() {
- return failed;
- }
-
- @Override
- public void run() {
- for (int i = 0; i < iter; i++) {
- final long actualStartDelay = randomDelay ? (long) random.nextInt(startDelay) : startDelay;
- final long actualHoldTime = randomDelay ? (long) random.nextInt(holdTime) : holdTime;
- Waiter.sleepQuietly(actualStartDelay);
- T obj = null;
- try {
- obj = pool.borrowObject();
- } catch (final Exception e) {
- error = e;
- failed = true;
- complete = true;
- break;
- }
-
- if (expectedObject != null && !expectedObject.equals(obj)) {
- error = new Throwable("Expected: " + expectedObject + " found: " + obj);
- failed = true;
- complete = true;
- break;
- }
-
- Waiter.sleepQuietly(actualHoldTime);
- try {
- pool.returnObject(obj);
- } catch (final Exception e) {
- error = e;
- failed = true;
- complete = true;
- break;
- }
- }
- complete = true;
- }
- }
-
- /*
- * Very simple test thread that just tries to borrow an object from
- * the provided pool returns it after a wait
- */
- static class WaitingTestThread<E extends Exception> extends Thread {
- private final GenericObjectPool<String, E> pool;
- private final long pause;
- private Throwable thrown;
-
- private long preBorrowMillis; // just before borrow
- private long postBorrowMillis; // borrow returned
- private long postReturnMillis; // after object was returned
- private long endedMillis;
- private String objectId;
-
- public WaitingTestThread(final GenericObjectPool<String, E> pool, final long pause) {
- this.pool = pool;
- this.pause = pause;
- this.thrown = null;
- }
-
- @Override
- public void run() {
- try {
- preBorrowMillis = System.currentTimeMillis();
- final String obj = pool.borrowObject();
- objectId = obj;
- postBorrowMillis = System.currentTimeMillis();
- Thread.sleep(pause);
- pool.returnObject(obj);
- postReturnMillis = System.currentTimeMillis();
- } catch (final Throwable e) {
- thrown = e;
- } finally{
- endedMillis = System.currentTimeMillis();
- }
- }
- }
-
- private static final boolean DISPLAY_THREAD_DETAILS=
- Boolean.parseBoolean(System.getProperty("TestGenericObjectPool.display.thread.details", "false"));
- // To pass this to a Maven test, use:
- // mvn test -DargLine="-DTestGenericObjectPool.display.thread.details=true"
- // @see https://issues.apache.org/jira/browse/SUREFIRE-121
-
- protected GenericObjectPool<String, TestException> genericObjectPool;
-
- private SimpleFactory simpleFactory;
-
- @SuppressWarnings("deprecation")
- private void assertConfiguration(final GenericObjectPoolConfig<?> expected, final GenericObjectPool<?, ?> actual) {
- assertEquals(Boolean.valueOf(expected.getTestOnCreate()), Boolean.valueOf(actual.getTestOnCreate()),
- "testOnCreate");
- assertEquals(Boolean.valueOf(expected.getTestOnBorrow()), Boolean.valueOf(actual.getTestOnBorrow()),
- "testOnBorrow");
- assertEquals(Boolean.valueOf(expected.getTestOnReturn()), Boolean.valueOf(actual.getTestOnReturn()),
- "testOnReturn");
- assertEquals(Boolean.valueOf(expected.getTestWhileIdle()), Boolean.valueOf(actual.getTestWhileIdle()),
- "testWhileIdle");
- assertEquals(Boolean.valueOf(expected.getBlockWhenExhausted()), Boolean.valueOf(actual.getBlockWhenExhausted()),
- "whenExhaustedAction");
- assertEquals(expected.getMaxTotal(), actual.getMaxTotal(), "maxTotal");
- assertEquals(expected.getMaxIdle(), actual.getMaxIdle(), "maxIdle");
- assertEquals(expected.getMaxWaitMillis(), actual.getMaxWaitMillis(), "maxWaitDuration");
- assertEquals(expected.getMaxWaitDuration(), actual.getMaxWaitDuration(), "maxWaitDuration");
- assertEquals(expected.getMinEvictableIdleTimeMillis(), actual.getMinEvictableIdleTimeMillis(),
- "minEvictableIdleTimeMillis");
- assertEquals(expected.getMinEvictableIdleTime(), actual.getMinEvictableIdleTime(),
- "minEvictableIdleTime");
- assertEquals(expected.getMinEvictableIdleDuration(), actual.getMinEvictableIdleDuration(),
- "minEvictableIdleDuration");
- assertEquals(expected.getNumTestsPerEvictionRun(), actual.getNumTestsPerEvictionRun(),
- "numTestsPerEvictionRun");
- assertEquals(expected.getEvictorShutdownTimeoutDuration(), actual.getEvictorShutdownTimeoutDuration(),
- "evictorShutdownTimeoutDuration");
- assertEquals(expected.getEvictorShutdownTimeoutMillis(), actual.getEvictorShutdownTimeoutMillis(),
- "evictorShutdownTimeoutMillis");
- assertEquals(expected.getEvictorShutdownTimeout(), actual.getEvictorShutdownTimeout(),
- "evictorShutdownTimeout");
- assertEquals(expected.getTimeBetweenEvictionRunsMillis(), actual.getTimeBetweenEvictionRunsMillis(),
- "timeBetweenEvictionRunsMillis");
- assertEquals(expected.getDurationBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(),
- "timeBetweenEvictionRuns");
- assertEquals(expected.getTimeBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(),
- "timeBetweenEvictionRuns");
- }
-
- private void checkEvict(final boolean lifo) throws Exception {
- // yea this is hairy but it tests all the code paths in GOP.evict()
- genericObjectPool.setSoftMinEvictableIdle(Duration.ofMillis(10));
- genericObjectPool.setSoftMinEvictableIdleTime(Duration.ofMillis(10));
- genericObjectPool.setMinIdle(2);
- genericObjectPool.setTestWhileIdle(true);
- genericObjectPool.setLifo(lifo);
- genericObjectPool.addObjects(5);
- genericObjectPool.evict();
- simpleFactory.setEvenValid(false);
- simpleFactory.setOddValid(false);
- simpleFactory.setThrowExceptionOnActivate(true);
- genericObjectPool.evict();
- genericObjectPool.addObjects(5);
- simpleFactory.setThrowExceptionOnActivate(false);
- simpleFactory.setThrowExceptionOnPassivate(true);
- genericObjectPool.evict();
- simpleFactory.setThrowExceptionOnPassivate(false);
- simpleFactory.setEvenValid(true);
- simpleFactory.setOddValid(true);
- Thread.sleep(125);
- genericObjectPool.evict();
- assertEquals(2, genericObjectPool.getNumIdle());
- }
-
- private void checkEvictionOrder(final boolean lifo) throws Exception {
- checkEvictionOrderPart1(lifo);
- tearDown();
- setUp();
- checkEvictionOrderPart2(lifo);
- }
-
- private void checkEvictionOrderPart1(final boolean lifo) throws Exception {
- genericObjectPool.setNumTestsPerEvictionRun(2);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
- genericObjectPool.setLifo(lifo);
- for (int i = 0; i < 5; i++) {
- genericObjectPool.addObject();
- Thread.sleep(100);
- }
- // Order, oldest to youngest, is "0", "1", ...,"4"
- genericObjectPool.evict(); // Should evict "0" and "1"
- final Object obj = genericObjectPool.borrowObject();
- assertNotEquals("0", obj, "oldest not evicted");
- assertNotEquals("1", obj, "second oldest not evicted");
- // 2 should be next out for FIFO, 4 for LIFO
- assertEquals(lifo ? "4" : "2" , obj,"Wrong instance returned");
- }
-
- private void checkEvictionOrderPart2(final boolean lifo) throws Exception {
- // Two eviction runs in sequence
- genericObjectPool.setNumTestsPerEvictionRun(2);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
- genericObjectPool.setLifo(lifo);
- for (int i = 0; i < 5; i++) {
- genericObjectPool.addObject();
- Thread.sleep(100);
- }
- genericObjectPool.evict(); // Should evict "0" and "1"
- genericObjectPool.evict(); // Should evict "2" and "3"
- final Object obj = genericObjectPool.borrowObject();
- assertEquals("4", obj,"Wrong instance remaining in pool");
- }
-
- private void checkEvictorVisiting(final boolean lifo) throws Exception {
- VisitTracker<Object> obj;
- VisitTrackerFactory<Object> trackerFactory = new VisitTrackerFactory<>();
- try (GenericObjectPool<VisitTracker<Object>,RuntimeException> trackerPool = new GenericObjectPool<>(trackerFactory)) {
- trackerPool.setNumTestsPerEvictionRun(2);
- trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
- trackerPool.setTestWhileIdle(true);
- trackerPool.setLifo(lifo);
- trackerPool.setTestOnReturn(false);
- trackerPool.setTestOnBorrow(false);
- for (int i = 0; i < 8; i++) {
- trackerPool.addObject();
- }
- trackerPool.evict(); // Visit oldest 2 - 0 and 1
- obj = trackerPool.borrowObject();
- trackerPool.returnObject(obj);
- obj = trackerPool.borrowObject();
- trackerPool.returnObject(obj);
- // borrow, return, borrow, return
- // FIFO will move 0 and 1 to end
- // LIFO, 7 out, then in, then out, then in
- trackerPool.evict(); // Should visit 2 and 3 in either case
- for (int i = 0; i < 8; i++) {
- final VisitTracker<Object> tracker = trackerPool.borrowObject();
- if (tracker.getId() >= 4) {
- assertEquals( 0, tracker.getValidateCount(),"Unexpected instance visited " + tracker.getId());
- } else {
- assertEquals( 1, tracker.getValidateCount(),
- "Instance " + tracker.getId() + " visited wrong number of times.");
- }
- }
- }
-
- trackerFactory = new VisitTrackerFactory<>();
- try (GenericObjectPool<VisitTracker<Object>, RuntimeException> trackerPool = new GenericObjectPool<>(trackerFactory)) {
- trackerPool.setNumTestsPerEvictionRun(3);
- trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
- trackerPool.setTestWhileIdle(true);
- trackerPool.setLifo(lifo);
- trackerPool.setTestOnReturn(false);
- trackerPool.setTestOnBorrow(false);
- for (int i = 0; i < 8; i++) {
- trackerPool.addObject();
- }
- trackerPool.evict(); // 0, 1, 2
- trackerPool.evict(); // 3, 4, 5
- obj = trackerPool.borrowObject();
- trackerPool.returnObject(obj);
- obj = trackerPool.borrowObject();
- trackerPool.returnObject(obj);
- obj = trackerPool.borrowObject();
- trackerPool.returnObject(obj);
- // borrow, return, borrow, return
- // FIFO 3,4,5,6,7,0,1,2
- // LIFO 7,6,5,4,3,2,1,0
- // In either case, pointer should be at 6
- trackerPool.evict();
- // Should hit 6,7,0 - 0 for second time
- for (int i = 0; i < 8; i++) {
- final VisitTracker<Object> tracker = trackerPool.borrowObject();
- if (tracker.getId() != 0) {
- assertEquals( 1, tracker.getValidateCount(),
- "Instance " + tracker.getId() + " visited wrong number of times.");
- } else {
- assertEquals( 2, tracker.getValidateCount(),
- "Instance " + tracker.getId() + " visited wrong number of times.");
- }
- }
- }
-
- // Randomly generate a pools with random numTests
- // and make sure evictor cycles through elements appropriately
- final int[] smallPrimes = { 2, 3, 5, 7 };
- final Random random = new Random();
- random.setSeed(System.currentTimeMillis());
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 5; j++) {
- try (GenericObjectPool<VisitTracker<Object>, RuntimeException> trackerPool = new GenericObjectPool<>(trackerFactory)) {
- trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]);
- trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
- trackerPool.setTestWhileIdle(true);
- trackerPool.setLifo(lifo);
- trackerPool.setTestOnReturn(false);
- trackerPool.setTestOnBorrow(false);
- trackerPool.setMaxIdle(-1);
- final int instanceCount = 10 + random.nextInt(20);
- trackerPool.setMaxTotal(instanceCount);
- for (int k = 0; k < instanceCount; k++) {
- trackerPool.addObject();
- }
-
- // Execute a random number of evictor runs
- final int runs = 10 + random.nextInt(50);
- for (int k = 0; k < runs; k++) {
- trackerPool.evict();
- }
-
- // Number of times evictor should have cycled through the pool
- final int cycleCount = (runs * trackerPool.getNumTestsPerEvictionRun()) / instanceCount;
-
- // Look at elements and make sure they are visited cycleCount
- // or cycleCount + 1 times
- VisitTracker<Object> tracker = null;
- int visitCount = 0;
- for (int k = 0; k < instanceCount; k++) {
- tracker = trackerPool.borrowObject();
- assertTrue(trackerPool.getNumActive() <= trackerPool.getMaxTotal());
- visitCount = tracker.getValidateCount();
- assertTrue(visitCount >= cycleCount && visitCount <= cycleCount + 1);
- }
- }
- }
- }
- }
-
- private BasePooledObjectFactory<String, RuntimeException> createDefaultPooledObjectFactory() {
- return new BasePooledObjectFactory<String, RuntimeException>() {
- @Override
- public String create() {
- // fake
- return null;
- }
-
- @Override
- public PooledObject<String> wrap(final String obj) {
- // fake
- return new DefaultPooledObject<>(obj);
- }
- };
- }
-
- private BasePooledObjectFactory<String, RuntimeException> createNullPooledObjectFactory() {
- return new BasePooledObjectFactory<String, RuntimeException>() {
- @Override
- public String create() {
- // fake
- return null;
- }
-
- @Override
- public PooledObject<String> wrap(final String obj) {
- // fake
- return null;
- }
- };
- }
-
- private BasePooledObjectFactory<String, InterruptedException> createSlowObjectFactory(final long elapsedTimeMillis) {
- return new BasePooledObjectFactory<String, InterruptedException>() {
- @Override
- public String create() throws InterruptedException {
- Thread.sleep(elapsedTimeMillis);
- return "created";
- }
-
- @Override
- public PooledObject<String> wrap(final String obj) {
- // fake
- return new DefaultPooledObject<>(obj);
- }
- };
- }
-
- @Override
- protected Object getNthObject(final int n) {
- return String.valueOf(n);
- }
-
- @Override
- protected boolean isFifo() {
- return false;
- }
-
- @Override
- protected boolean isLifo() {
- return true;
- }
-
- @Override
- protected ObjectPool<String, TestException> makeEmptyPool(final int minCap) {
- final GenericObjectPool<String, TestException> mtPool = new GenericObjectPool<>(new SimpleFactory());
- mtPool.setMaxTotal(minCap);
- mtPool.setMaxIdle(minCap);
- return mtPool;
- }
-
- @Override
- protected <E extends Exception> ObjectPool<Object, E> makeEmptyPool(final PooledObjectFactory<Object, E> fac) {
- return new GenericObjectPool<>(fac);
- }
-
- /**
- * Kicks off <numThreads> test threads, each of which will go through
- * <iterations> borrow-return cycles with random delay times <= delay
- * in between.
- */
- private <T, E extends Exception> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericObjectPool<T, E> testPool) {
- final TestThread<T, E>[] threads = new TestThread[numThreads];
- for (int i = 0; i < numThreads; i++) {
- threads[i] = new TestThread<>(testPool, iterations, delay);
- final Thread t = new Thread(threads[i]);
- t.start();
- }
- for (int i = 0; i < numThreads; i++) {
- while (!(threads[i]).complete()) {
- Waiter.sleepQuietly(500L);
- }
- if (threads[i].failed()) {
- fail("Thread " + i + " failed: " + threads[i].error.toString());
- }
- }
- }
-
- @BeforeEach
- public void setUp() {
- simpleFactory = new SimpleFactory();
- genericObjectPool = new GenericObjectPool<>(simpleFactory);
- }
-
- @AfterEach
- public void tearDown() throws Exception {
- final ObjectName jmxName = genericObjectPool.getJmxName();
- final String poolName = Objects.toString(jmxName, null);
-
- genericObjectPool.clear();
- genericObjectPool.close();
- genericObjectPool = null;
- simpleFactory = null;
-
- final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- final Set<ObjectName> result = mbs.queryNames(new ObjectName("org.apache.commoms.pool2:type=GenericObjectPool,*"), null);
- // There should be no registered pools at this point
- final int registeredPoolCount = result.size();
- final StringBuilder msg = new StringBuilder("Current pool is: ");
- msg.append(poolName);
- msg.append(" Still open pools are: ");
- for (final ObjectName name : result) {
- // Clean these up ready for the next test
- msg.append(name.toString());
- msg.append(" created via\n");
- msg.append(mbs.getAttribute(name, "CreationStackTrace"));
- msg.append('\n');
- mbs.unregisterMBean(name);
- }
- assertEquals(0, registeredPoolCount, msg.toString());
-
- // Make sure that EvictionTimer executor is shut down.
- Thread.yield();
- if (EvictionTimer.getExecutor() != null) {
- Thread.sleep(1000);
- }
- assertNull(EvictionTimer.getExecutor(), "EvictionTimer.getExecutor()");
- }
-
- /**
- * Check that a pool that starts an evictor, but is never closed does not leave EvictionTimer executor running. Confirmation check is in
- * {@link #tearDown()}.
- *
- * @throws TestException Custom exception
- * @throws InterruptedException if any thread has interrupted the current thread. The <em>interrupted status</em> of the current thread is cleared when this
- * exception is thrown.
- */
- @SuppressWarnings("deprecation")
- @Test
- public void testAbandonedPool() throws TestException, InterruptedException {
- final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
- config.setJmxEnabled(false);
- GenericObjectPool<String, TestException> abandoned = new GenericObjectPool<>(simpleFactory, config);
- abandoned.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
- assertEquals(abandoned.getRemoveAbandonedTimeout(), abandoned.getRemoveAbandonedTimeoutDuration().getSeconds());
-
- // This is ugly, but forces GC to hit the pool
- final WeakReference<GenericObjectPool<String, TestException>> ref = new WeakReference<>(abandoned);
- abandoned = null;
- while (ref.get() != null) {
- System.gc();
- Thread.sleep(100);
- }
- }
-
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testAddObject() throws Exception {
- assertEquals( 0, genericObjectPool.getNumIdle(),"should be zero idle");
- genericObjectPool.addObject();
- assertEquals( 1, genericObjectPool.getNumIdle(),"should be one idle");
- assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active");
- final String obj = genericObjectPool.borrowObject();
- assertEquals( 0, genericObjectPool.getNumIdle(),"should be zero idle");
- assertEquals( 1, genericObjectPool.getNumActive(),"should be one active");
- genericObjectPool.returnObject(obj);
- assertEquals( 1, genericObjectPool.getNumIdle(),"should be one idle");
- assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active");
- }
-
- @Test
- public void testAppendStats() {
- assertFalse(genericObjectPool.getMessageStatistics());
- assertEquals("foo", (genericObjectPool.appendStats("foo")));
- try (final GenericObjectPool<?, TestException> 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
- * aware of this if you see a failure of this test.
- */
- @SuppressWarnings({
- "rawtypes", "unchecked"
- })
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testBorrowObjectFairness() throws Exception {
-
- final int numThreads = 40;
- final int maxTotal = 40;
-
- final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
- config.setMaxTotal(maxTotal);
- config.setMaxIdle(maxTotal);
- config.setFairness(true);
- config.setLifo(false);
-
- genericObjectPool = new GenericObjectPool(simpleFactory, config);
-
- // Exhaust the pool
- final String[] objects = new String[maxTotal];
- for (int i = 0; i < maxTotal; i++) {
- objects[i] = genericObjectPool.borrowObject();
- }
-
- // Start and park threads waiting to borrow objects
- final TestThread[] threads = new TestThread[numThreads];
- for(int i=0;i<numThreads;i++) {
- threads[i] = new TestThread(genericObjectPool, 1, 0, 2000, false, String.valueOf(i % maxTotal));
- final Thread t = new Thread(threads[i]);
- t.start();
- // Short delay to ensure threads start in correct order
- try {
- Thread.sleep(10);
- } catch (final InterruptedException e) {
- fail(e.toString());
- }
- }
-
- // Return objects, other threads should get served in order
- for (int i = 0; i < maxTotal; i++) {
- genericObjectPool.returnObject(objects[i]);
- }
-
- // Wait for threads to finish
- for (int i = 0; i < numThreads; i++) {
- while (!(threads[i]).complete()) {
- Waiter.sleepQuietly(500L);
- }
- if (threads[i].failed()) {
- fail("Thread " + i + " failed: " + threads[i].error.toString());
- }
- }
- }
-
- @Test
- public void testBorrowTimings() throws Exception {
- // Borrow
- final String object = genericObjectPool.borrowObject();
- final PooledObject<String> po = genericObjectPool.getPooledObject(object);
- // In the initial state, all instants are the creation instant: last borrow, last use, last return.
- // In the initial state, the active duration is the time between "now" and the creation time.
- // In the initial state, the idle duration is the time between "now" and the last return, which is the creation time.
- // But... this PO might have already been used in other tests in this class.
-
- final Instant lastBorrowInstant1 = po.getLastBorrowInstant();
- final Instant lastReturnInstant1 = po.getLastReturnInstant();
- final Instant lastUsedInstant1 = po.getLastUsedInstant();
-
- assertThat(po.getCreateInstant(), lessThanOrEqualTo(lastBorrowInstant1));
- assertThat(po.getCreateInstant(), lessThanOrEqualTo(lastReturnInstant1));
- assertThat(po.getCreateInstant(), lessThanOrEqualTo(lastUsedInstant1));
- assertThat(po.getCreateTime(), lessThanOrEqualTo(lastBorrowInstant1.toEpochMilli()));
- assertThat(po.getCreateTime(), lessThanOrEqualTo(lastReturnInstant1.toEpochMilli()));
- assertThat(po.getCreateTime(), lessThanOrEqualTo(lastUsedInstant1.toEpochMilli()));
-
- // Sleep MUST be "long enough" to detect that more than 0 milliseconds have elapsed.
- // Need an API in Java 8 to get the clock granularity.
- Thread.sleep(200);
-
- assertFalse(po.getActiveDuration().isNegative());
- assertFalse(po.getActiveDuration().isZero());
- // We use greaterThanOrEqualTo instead of equal because "now" many be different when each argument is evaluated.
- assertThat(1L, lessThanOrEqualTo(2L)); // sanity check
- assertThat(Duration.ZERO, lessThanOrEqualTo(Duration.ZERO.plusNanos(1))); // sanity check
- assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getIdleDuration()));
- // Deprecated
- assertThat(po.getActiveDuration().toMillis(), lessThanOrEqualTo(po.getActiveTimeMillis()));
- assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getActiveTime()));
- //
- // TODO How to compare ID with AD since other tests may have touched the PO?
- assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getIdleTime()));
- assertThat(po.getActiveDuration().toMillis(), lessThanOrEqualTo(po.getIdleTimeMillis()));
- //
- assertThat(po.getCreateInstant(), lessThanOrEqualTo(po.getLastBorrowInstant()));
- assertThat(po.getCreateInstant(), lessThanOrEqualTo(po.getLastReturnInstant()));
- assertThat(po.getCreateInstant(), lessThanOrEqualTo(po.getLastUsedInstant()));
-
- assertThat(lastBorrowInstant1, lessThanOrEqualTo(po.getLastBorrowInstant()));
- assertThat(lastReturnInstant1, lessThanOrEqualTo(po.getLastReturnInstant()));
- assertThat(lastUsedInstant1, lessThanOrEqualTo(po.getLastUsedInstant()));
-
- genericObjectPool.returnObject(object);
-
- assertFalse(po.getActiveDuration().isNegative());
- assertFalse(po.getActiveDuration().isZero());
- assertThat(po.getActiveDuration().toMillis(), lessThanOrEqualTo(po.getActiveTimeMillis()));
- assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getActiveTime()));
-
- assertThat(lastBorrowInstant1, lessThanOrEqualTo(po.getLastBorrowInstant()));
- assertThat(lastReturnInstant1, lessThanOrEqualTo(po.getLastReturnInstant()));
- assertThat(lastUsedInstant1, lessThanOrEqualTo(po.getLastUsedInstant()));
- }
-
- /**
- * On first borrow, first object fails validation, second object is OK.
- * Subsequent borrows are OK. This was POOL-152.
- * @throws Exception
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testBrokenFactoryShouldNotBlockPool() throws Exception {
- final int maxTotal = 1;
-
- simpleFactory.setMaxTotal(maxTotal);
- genericObjectPool.setMaxTotal(maxTotal);
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setTestOnBorrow(true);
-
- // First borrow object will need to create a new object which will fail
- // validation.
- String obj = null;
- Exception ex = null;
- simpleFactory.setValid(false);
- try {
- obj = genericObjectPool.borrowObject();
- } catch (final Exception e) {
- ex = e;
- }
- // Failure expected
- assertNotNull(ex);
- assertTrue(ex instanceof NoSuchElementException);
- assertNull(obj);
-
- // Configure factory to create valid objects so subsequent borrows work
- simpleFactory.setValid(true);
-
- // Subsequent borrows should be OK
- obj = genericObjectPool.borrowObject();
- assertNotNull(obj);
- genericObjectPool.returnObject(obj);
- }
-
- // POOL-259
- @Test
- public void testClientWaitStats() throws TestException {
- final SimpleFactory factory = new SimpleFactory();
- // Give makeObject a little latency
- factory.setMakeLatency(200);
- try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
- final String s = pool.borrowObject();
- // First borrow waits on create, so wait time should be at least 200 ms
- // Allow 100ms error in clock times
- assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100);
- assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100);
- pool.returnObject(s);
- pool.borrowObject();
- // Second borrow does not have to wait on create, average should be about 100
- assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100);
- assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200);
- assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20);
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testCloseMultiplePools1() {
- try (final GenericObjectPool<String, TestException> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
- genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
- genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
- }
- genericObjectPool.close();
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testCloseMultiplePools2() throws Exception {
- try (final GenericObjectPool<String, TestException> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
- // Ensure eviction takes a long time, during which time EvictionTimer.executor's queue is empty
- simpleFactory.setDestroyLatency(1000L);
- // Ensure there is an object to evict, so that above latency takes effect
- genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
- genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
- genericObjectPool.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
- genericObjectPool2.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
- genericObjectPool.addObject();
- genericObjectPool2.addObject();
- // Close both pools
- }
- genericObjectPool.close();
- }
-
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testConcurrentBorrowAndEvict() throws Exception {
-
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.addObject();
-
- for (int i = 0; i < 5000; i++) {
- final ConcurrentBorrowAndEvictThread one =
- new ConcurrentBorrowAndEvictThread(true);
- final ConcurrentBorrowAndEvictThread two =
- new ConcurrentBorrowAndEvictThread(false);
-
- one.start();
- two.start();
- one.join();
- two.join();
-
- genericObjectPool.returnObject(one.obj);
-
- /* Uncomment this for a progress indication
- if (i % 10 == 0) {
- System.out.println(i/10);
- }
- */
- }
- }
-
- /**
- * POOL-231 - verify that concurrent invalidates of the same object do not
- * corrupt pool destroyCount.
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- public void testConcurrentInvalidate() throws Exception {
- // Get allObjects and idleObjects loaded with some instances
- final int nObjects = 1000;
- genericObjectPool.setMaxTotal(nObjects);
- genericObjectPool.setMaxIdle(nObjects);
- final String[] obj = new String[nObjects];
- for (int i = 0; i < nObjects; i++) {
- obj[i] = genericObjectPool.borrowObject();
- }
- for (int i = 0; i < nObjects; i++) {
- if (i % 2 == 0) {
- genericObjectPool.returnObject(obj[i]);
- }
- }
- final int nThreads = 20;
- final int nIterations = 60;
- final InvalidateThread[] threads = new InvalidateThread[nThreads];
- // Randomly generated list of distinct invalidation targets
- final ArrayList<Integer> targets = new ArrayList<>();
- final Random random = new Random();
- for (int j = 0; j < nIterations; j++) {
- // Get a random invalidation target
- Integer targ = Integer.valueOf(random.nextInt(nObjects));
- while (targets.contains(targ)) {
- targ = Integer.valueOf(random.nextInt(nObjects));
- }
- targets.add(targ);
- // Launch nThreads threads all trying to invalidate the target
- for (int i = 0; i < nThreads; i++) {
- threads[i] = new InvalidateThread(genericObjectPool, obj[targ.intValue()]);
- }
- for (int i = 0; i < nThreads; i++) {
- new Thread(threads[i]).start();
- }
- boolean done = false;
- while (!done) {
- done = true;
- for (int i = 0; i < nThreads; i++) {
- done = done && threads[i].complete();
- }
- Thread.sleep(100);
- }
- }
- assertEquals(nIterations, genericObjectPool.getDestroyedCount());
- }
-
- @Test
- public void testConstructorNullFactory() {
- // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
- assertThrows(IllegalArgumentException.class,
- () -> new GenericObjectPool<>(null));
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testConstructors() {
-
- // Make constructor arguments all different from defaults
- final int minIdle = 2;
- final Duration maxWaitDuration = Duration.ofMillis(3);
- final long maxWaitMillis = maxWaitDuration.toMillis();
- final int maxIdle = 4;
- final int maxTotal = 5;
- final Duration minEvictableIdleDuration = Duration.ofMillis(6);
- final long minEvictableIdleMillis = minEvictableIdleDuration.toMillis();
- final int numTestsPerEvictionRun = 7;
- final boolean testOnBorrow = true;
- final boolean testOnReturn = true;
- final boolean testWhileIdle = true;
- final long timeBetweenEvictionRunsMillis = 8;
- final boolean blockWhenExhausted = false;
- final boolean lifo = false;
- final PooledObjectFactory<Object, RuntimeException> dummyFactory = new DummyFactory();
- try (GenericObjectPool<Object, RuntimeException> dummyPool = new GenericObjectPool<>(dummyFactory)) {
- assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_IDLE, dummyPool.getMaxIdle());
- assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, dummyPool.getMaxWaitMillis());
- assertEquals(GenericObjectPoolConfig.DEFAULT_MIN_IDLE, dummyPool.getMinIdle());
- assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal());
- assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
- dummyPool.getMinEvictableIdleTimeMillis());
- assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
- dummyPool.getMinEvictableIdleTime());
- assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
- dummyPool.getMinEvictableIdleDuration());
- assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
- dummyPool.getNumTestsPerEvictionRun());
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW),
- Boolean.valueOf(dummyPool.getTestOnBorrow()));
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN),
- Boolean.valueOf(dummyPool.getTestOnReturn()));
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE),
- Boolean.valueOf(dummyPool.getTestWhileIdle()));
- assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
- dummyPool.getDurationBetweenEvictionRuns());
- assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
- dummyPool.getTimeBetweenEvictionRunsMillis());
- assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
- dummyPool.getTimeBetweenEvictionRuns());
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED),
- Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo()));
- }
-
- final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
- config.setLifo(lifo);
- config.setMaxIdle(maxIdle);
- config.setMinIdle(minIdle);
- config.setMaxTotal(maxTotal);
- config.setMaxWait(maxWaitDuration);
- config.setMinEvictableIdleTimeMillis(minEvictableIdleMillis);
- assertEquals(minEvictableIdleMillis, config.getMinEvictableIdleTime().toMillis());
- config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
- config.setTestOnBorrow(testOnBorrow);
- config.setTestOnReturn(testOnReturn);
- config.setTestWhileIdle(testWhileIdle);
- config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
- assertEquals(timeBetweenEvictionRunsMillis, config.getTimeBetweenEvictionRuns().toMillis());
- config.setBlockWhenExhausted(blockWhenExhausted);
- try (GenericObjectPool<Object, RuntimeException> dummyPool = new GenericObjectPool<>(dummyFactory, config)) {
- assertEquals(maxIdle, dummyPool.getMaxIdle());
- assertEquals(maxWaitDuration, dummyPool.getMaxWaitDuration());
- assertEquals(maxWaitMillis, dummyPool.getMaxWaitMillis());
- assertEquals(minIdle, dummyPool.getMinIdle());
- assertEquals(maxTotal, dummyPool.getMaxTotal());
- assertEquals(minEvictableIdleMillis, dummyPool.getMinEvictableIdleTimeMillis());
- assertEquals(numTestsPerEvictionRun, dummyPool.getNumTestsPerEvictionRun());
- assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(dummyPool.getTestOnBorrow()));
- assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(dummyPool.getTestOnReturn()));
- assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(dummyPool.getTestWhileIdle()));
- assertEquals(timeBetweenEvictionRunsMillis, dummyPool.getTimeBetweenEvictionRunsMillis());
- assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
- assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(dummyPool.getLifo()));
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testDefaultConfiguration() {
- assertConfiguration(new GenericObjectPoolConfig<>(),genericObjectPool);
- }
-
- /**
- * Verifies that when a factory's makeObject produces instances that are not
- * discernible by equals, the pool can handle them.
- *
- * JIRA: POOL-283
- */
- @Test
- public void testEqualsIndiscernible() throws Exception {
- final HashSetFactory factory = new HashSetFactory();
- try (final GenericObjectPool<HashSet<String>, RuntimeException> pool = new GenericObjectPool<>(factory,
- new GenericObjectPoolConfig<>())) {
- final HashSet<String> s1 = pool.borrowObject();
- final HashSet<String> s2 = pool.borrowObject();
- pool.returnObject(s1);
- pool.returnObject(s2);
- }
- }
-
- @Test
- public void testErrorFactoryDoesNotBlockThreads() throws Exception {
-
- final CreateErrorFactory factory = new CreateErrorFactory();
- try (final GenericObjectPool<String, InterruptedException> createFailFactoryPool = new GenericObjectPool<>(factory)) {
-
- createFailFactoryPool.setMaxTotal(1);
-
- // Try and borrow the first object from the pool
- final WaitingTestThread<InterruptedException> thread1 = new WaitingTestThread<>(createFailFactoryPool, 0);
- thread1.start();
-
- // Wait for thread to reach semaphore
- while (!factory.hasQueuedThreads()) {
- Thread.sleep(200);
- }
-
- // Try and borrow the second object from the pool
- final WaitingTestThread<InterruptedException> thread2 = new WaitingTestThread<>(createFailFactoryPool, 0);
- thread2.start();
- // Pool will not call factory since maximum number of object creations
- // are already queued.
-
- // Thread 2 will wait on an object being returned to the pool
- // Give thread 2 a chance to reach this state
- Thread.sleep(1000);
-
- // Release thread1
- factory.release();
- // Pre-release thread2
- factory.release();
-
- // Both threads should now complete.
- boolean threadRunning = true;
- int count = 0;
- while (threadRunning && count < 15) {
- threadRunning = thread1.isAlive();
- threadRunning = thread2.isAlive();
- Thread.sleep(200);
- count++;
- }
- assertFalse(thread1.isAlive());
- assertFalse(thread2.isAlive());
-
- assertTrue(thread1.thrown instanceof UnknownError);
- assertTrue(thread2.thrown instanceof UnknownError);
- }
- }
-
- /**
- * Tests addObject contention between ensureMinIdle triggered by
- * the Evictor with minIdle > 0 and borrowObject.
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictAddObjects() throws Exception {
- simpleFactory.setMakeLatency(300);
- simpleFactory.setMaxTotal(2);
- genericObjectPool.setMaxTotal(2);
- genericObjectPool.setMinIdle(1);
- genericObjectPool.borrowObject(); // numActive = 1, numIdle = 0
- // Create a test thread that will run once and try a borrow after
- // 150ms fixed delay
- final TestThread<String, TestException> borrower = new TestThread<>(genericObjectPool, 1, 150, false);
- final Thread borrowerThread = new Thread(borrower);
- // Set evictor to run in 100 ms - will create idle instance
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- borrowerThread.start(); // Off to the races
- borrowerThread.join();
- assertFalse(borrower.failed());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictFIFO() throws Exception {
- checkEvict(false);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEviction() throws Exception {
- genericObjectPool.setMaxIdle(500);
- genericObjectPool.setMaxTotal(500);
- genericObjectPool.setNumTestsPerEvictionRun(100);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
- genericObjectPool.setTestWhileIdle(true);
-
- final String[] active = new String[500];
- for (int i = 0; i < 500; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- for (int i = 0; i < 500; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- Waiter.sleepQuietly(1000L);
- assertTrue(genericObjectPool.getNumIdle() < 500,"Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 400,"Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 300,"Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 200,"Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 100,"Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle());
-
- for (int i = 0; i < 500; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- for (int i = 0; i < 500; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- Waiter.sleepQuietly(1000L);
- assertTrue(genericObjectPool.getNumIdle() < 500,"Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 400,"Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 300,"Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 200,"Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertTrue(genericObjectPool.getNumIdle() < 100,"Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(600L);
- assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictionInvalid() throws Exception {
-
- try (final GenericObjectPool<Object, RuntimeException> invalidFactoryPool = new GenericObjectPool<>(new InvalidFactory())) {
-
- invalidFactoryPool.setMaxIdle(1);
- invalidFactoryPool.setMaxTotal(1);
- invalidFactoryPool.setTestOnBorrow(false);
- invalidFactoryPool.setTestOnReturn(false);
- invalidFactoryPool.setTestWhileIdle(true);
- invalidFactoryPool.setMinEvictableIdleTime(Duration.ofSeconds(100));
- invalidFactoryPool.setNumTestsPerEvictionRun(1);
-
- final Object p = invalidFactoryPool.borrowObject();
- invalidFactoryPool.returnObject(p);
-
- // Run eviction in a separate thread
- final Thread t = new EvictionThread<>(invalidFactoryPool);
- t.start();
-
- // Sleep to make sure evictor has started
- Thread.sleep(300);
-
- try {
- invalidFactoryPool.borrowObject(1);
- } catch (final NoSuchElementException nsee) {
- // Ignore
- }
-
- // Make sure evictor has finished
- Thread.sleep(1000);
-
- // Should have an empty pool
- assertEquals( 0, invalidFactoryPool.getNumIdle(),"Idle count different than expected.");
- assertEquals( 0, invalidFactoryPool.getNumActive(),"Total count different than expected.");
- }
- }
-
- /**
- * Test to make sure evictor visits least recently used objects first,
- * regardless of FIFO/LIFO.
- *
- * JIRA: POOL-86
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictionOrder() throws Exception {
- checkEvictionOrder(false);
- tearDown();
- setUp();
- checkEvictionOrder(true);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictionPolicy() throws Exception {
- genericObjectPool.setMaxIdle(500);
- genericObjectPool.setMaxTotal(500);
- genericObjectPool.setNumTestsPerEvictionRun(500);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
- genericObjectPool.setTestWhileIdle(true);
-
- // ClassNotFoundException
- assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Long.toString(System.currentTimeMillis())),
- "setEvictionPolicyClassName must throw an error if the class name is invalid.");
-
- // InstantiationException
- assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(java.io.Serializable.class.getName()),
- "setEvictionPolicyClassName must throw an error if the class name is invalid.");
-
- // IllegalAccessException
- assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(java.util.Collections.class.getName()),
- "setEvictionPolicyClassName must throw an error if the class name is invalid.");
-
- assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(java.lang.String.class.getName()),
- () -> "setEvictionPolicyClassName must throw an error if a class that does not implement EvictionPolicy is specified.");
-
- genericObjectPool.setEvictionPolicy(new TestEvictionPolicy<>());
- assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
-
- genericObjectPool.setEvictionPolicyClassName(TestEvictionPolicy.class.getName());
- assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
-
- final String[] active = new String[500];
- for (int i = 0; i < 500; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- for (int i = 0; i < 500; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- // Eviction policy ignores first 1500 attempts to evict and then always
- // evicts. After 1s, there should have been two runs of 500 tests so no
- // evictions
- Waiter.sleepQuietly(1000L);
- assertEquals(500, genericObjectPool.getNumIdle(), "Should be 500 idle");
- // A further 1s wasn't enough so allow 2s for the evictor to clear out
- // all of the idle objects.
- Waiter.sleepQuietly(2000L);
- assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle");
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictionSoftMinIdle() throws Exception {
- class TimeTest extends BasePooledObjectFactory<TimeTest, RuntimeException> {
- private final long createTimeMillis;
-
- public TimeTest() {
- createTimeMillis = System.currentTimeMillis();
- }
-
- @Override
- public TimeTest create() {
- return new TimeTest();
- }
-
- public long getCreateTimeMillis() {
- return createTimeMillis;
- }
-
- @Override
- public PooledObject<TimeTest> wrap(final TimeTest value) {
- return new DefaultPooledObject<>(value);
- }
- }
-
- try (final GenericObjectPool<TimeTest, RuntimeException> timePool = new GenericObjectPool<>(new TimeTest())) {
-
- timePool.setMaxIdle(5);
- timePool.setMaxTotal(5);
- timePool.setNumTestsPerEvictionRun(5);
- timePool.setMinEvictableIdle(Duration.ofSeconds(3));
- timePool.setMinEvictableIdleTime(Duration.ofSeconds(3));
- timePool.setSoftMinEvictableIdleTime(TestConstants.ONE_SECOND_DURATION);
- timePool.setMinIdle(2);
-
- final TimeTest[] active = new TimeTest[5];
- final Long[] creationTime = new Long[5];
- for (int i = 0; i < 5; i++) {
- active[i] = timePool.borrowObject();
- creationTime[i] = Long.valueOf((active[i]).getCreateTimeMillis());
- }
-
- for (int i = 0; i < 5; i++) {
- timePool.returnObject(active[i]);
- }
-
- // Soft evict all but minIdle(2)
- Thread.sleep(1500L);
- timePool.evict();
- assertEquals( 2, timePool.getNumIdle(),"Idle count different than expected.");
-
- // Hard evict the rest.
- Thread.sleep(2000L);
- timePool.evict();
- assertEquals( 0, timePool.getNumIdle(),"Idle count different than expected.");
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictionWithNegativeNumTests() throws Exception {
- // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test
- genericObjectPool.setMaxIdle(6);
- genericObjectPool.setMaxTotal(6);
- genericObjectPool.setNumTestsPerEvictionRun(-2);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
-
- final String[] active = new String[6];
- for (int i = 0; i < 6; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- for (int i = 0; i < 6; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- Waiter.sleepQuietly(100L);
- assertTrue(genericObjectPool.getNumIdle() <= 6,"Should at most 6 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(100L);
- assertTrue(genericObjectPool.getNumIdle() <= 3,"Should at most 3 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(100L);
- assertTrue(genericObjectPool.getNumIdle() <= 2,"Should be at most 2 idle, found " + genericObjectPool.getNumIdle());
- Waiter.sleepQuietly(100L);
- assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictLIFO() throws Exception {
- checkEvict(true);
- }
-
- /**
- * Verifies that the evictor visits objects in expected order
- * and frequency.
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- public void testEvictorVisiting() throws Exception {
- checkEvictorVisiting(true);
- checkEvictorVisiting(false);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictWhileEmpty() throws Exception {
- genericObjectPool.evict();
- genericObjectPool.evict();
- genericObjectPool.close();
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionInValidationDuringEviction() throws Exception {
- genericObjectPool.setMaxIdle(1);
- genericObjectPool.setMinEvictableIdleTime(Duration.ZERO);
- genericObjectPool.setTestWhileIdle(true);
-
- final String active = genericObjectPool.borrowObject();
- genericObjectPool.returnObject(active);
-
- simpleFactory.setThrowExceptionOnValidate(true);
-
- assertThrows(RuntimeException.class, () -> genericObjectPool.evict());
- assertEquals(0, genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionOnActivateDuringBorrow() throws Exception {
- final String obj1 = genericObjectPool.borrowObject();
- final String obj2 = genericObjectPool.borrowObject();
- genericObjectPool.returnObject(obj1);
- genericObjectPool.returnObject(obj2);
- simpleFactory.setThrowExceptionOnActivate(true);
- simpleFactory.setEvenValid(false);
- // Activation will now throw every other time
- // First attempt throws, but loop continues and second succeeds
- final String obj = genericObjectPool.borrowObject();
- assertEquals(1, genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
-
- genericObjectPool.returnObject(obj);
- simpleFactory.setValid(false);
- // Validation will now fail on activation when borrowObject returns
- // an idle instance, and then when attempting to create a new instance
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- assertEquals(0, genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionOnDestroyDuringBorrow() throws Exception {
- simpleFactory.setThrowExceptionOnDestroy(true);
- genericObjectPool.setTestOnBorrow(true);
- genericObjectPool.borrowObject();
- simpleFactory.setValid(false); // Make validation fail on next borrow attempt
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- assertEquals(1, genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionOnDestroyDuringReturn() throws Exception {
- simpleFactory.setThrowExceptionOnDestroy(true);
- genericObjectPool.setTestOnReturn(true);
- final String obj1 = genericObjectPool.borrowObject();
- genericObjectPool.borrowObject();
- simpleFactory.setValid(false); // Make validation fail
- genericObjectPool.returnObject(obj1);
- assertEquals(1, genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionOnPassivateDuringReturn() throws Exception {
- final String obj = genericObjectPool.borrowObject();
- simpleFactory.setThrowExceptionOnPassivate(true);
- genericObjectPool.returnObject(obj);
- assertEquals(0,genericObjectPool.getNumIdle());
- }
-
- @Test
- public void testFailingFactoryDoesNotBlockThreads() throws Exception {
-
- final CreateFailFactory factory = new CreateFailFactory();
- try (final GenericObjectPool<String, InterruptedException> createFailFactoryPool = new GenericObjectPool<>(factory)) {
-
- createFailFactoryPool.setMaxTotal(1);
-
- // Try and borrow the first object from the pool
- final WaitingTestThread<InterruptedException> thread1 = new WaitingTestThread<>(createFailFactoryPool, 0);
- thread1.start();
-
- // Wait for thread to reach semaphore
- while (!factory.hasQueuedThreads()) {
- Thread.sleep(200);
- }
-
- // Try and borrow the second object from the pool
- final WaitingTestThread<InterruptedException> thread2 = new WaitingTestThread<>(createFailFactoryPool, 0);
- thread2.start();
- // Pool will not call factory since maximum number of object creations
- // are already queued.
-
- // Thread 2 will wait on an object being returned to the pool
- // Give thread 2 a chance to reach this state
- Thread.sleep(1000);
-
- // Release thread1
- factory.release();
- // Pre-release thread2
- factory.release();
-
- // Both threads should now complete.
- boolean threadRunning = true;
- int count = 0;
- while (threadRunning && count < 15) {
- threadRunning = thread1.isAlive();
- threadRunning = thread2.isAlive();
- Thread.sleep(200);
- count++;
- }
- assertFalse(thread1.isAlive());
- assertFalse(thread2.isAlive());
-
- assertTrue(thread1.thrown instanceof UnsupportedCharsetException);
- assertTrue(thread2.thrown instanceof UnsupportedCharsetException);
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testFIFO() throws Exception {
- genericObjectPool.setLifo(false);
- genericObjectPool.addObject(); // "0"
- genericObjectPool.addObject(); // "1"
- genericObjectPool.addObject(); // "2"
- assertEquals( "0", genericObjectPool.borrowObject(),"Oldest");
- assertEquals( "1", genericObjectPool.borrowObject(),"Middle");
- assertEquals( "2", genericObjectPool.borrowObject(),"Youngest");
- final String o = genericObjectPool.borrowObject();
- assertEquals( "3", o,"new-3");
- genericObjectPool.returnObject(o);
- assertEquals( o, genericObjectPool.borrowObject(),"returned-3");
- assertEquals( "4", genericObjectPool.borrowObject(),"new-4");
- }
-
- @Test
- public void testGetFactoryType_DefaultPooledObjectFactory() {
- try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(createDefaultPooledObjectFactory())) {
- assertNotNull((pool.getFactoryType()));
- }
- }
-
- @Test
- public void testGetFactoryType_NullPooledObjectFactory() {
- try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(createNullPooledObjectFactory())) {
- assertNotNull((pool.getFactoryType()));
- }
- }
-
- @Test
- public void testGetFactoryType_PoolUtilsSynchronizedDefaultPooledFactory() {
- try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
- PoolUtils.synchronizedPooledFactory(createDefaultPooledObjectFactory()))) {
- assertNotNull((pool.getFactoryType()));
- }
- }
-
- @Test
- public void testGetFactoryType_PoolUtilsSynchronizedNullPooledFactory() {
- try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
- PoolUtils.synchronizedPooledFactory(createNullPooledObjectFactory()))) {
- assertNotNull((pool.getFactoryType()));
- }
- }
-
- @Test
- public void testGetFactoryType_SynchronizedDefaultPooledObjectFactory() {
- try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
- new TestSynchronizedPooledObjectFactory<>(createDefaultPooledObjectFactory()))) {
- assertNotNull((pool.getFactoryType()));
- }
- }
-
- @Test
- public void testGetFactoryType_SynchronizedNullPooledObjectFactory() {
- try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
- new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
- assertNotNull((pool.getFactoryType()));
- }
- }
-
- @Test
- public void testGetStatsString() {
- try (final GenericObjectPool<String, RuntimeException> 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.
- *
- * JIRA: POOL-240
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- public void testInvalidateFreesCapacity() throws Exception {
- final SimpleFactory factory = new SimpleFactory();
- try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(factory)) {
- pool.setMaxTotal(2);
- pool.setMaxWaitMillis(500);
- // Borrow an instance and hold if for 5 seconds
- final WaitingTestThread<TestException> thread1 = new WaitingTestThread<>(pool, 5000);
- thread1.start();
- // Borrow another instance
- final String obj = pool.borrowObject();
- // Launch another thread - will block, but fail in 500 ms
- final WaitingTestThread<TestException> thread2 = new WaitingTestThread<>(pool, 100);
- thread2.start();
- // Invalidate the object borrowed by this thread - should allow thread2 to create
- Thread.sleep(20);
- pool.invalidateObject(obj);
- Thread.sleep(600); // Wait for thread2 to timeout
- if (thread2.thrown != null) {
- fail(thread2.thrown.toString());
- }
- }
- }
-
- /**
- * Ensure the pool is registered.
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testJmxRegistration() {
- final ObjectName oname = genericObjectPool.getJmxName();
- final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- final Set<ObjectName> result = mbs.queryNames(oname, null);
- assertEquals(1, result.size());
- genericObjectPool.jmxUnregister();
-
- final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
- config.setJmxEnabled(false);
- try (final GenericObjectPool<String, TestException> poolWithoutJmx = new GenericObjectPool<>(simpleFactory, config)) {
- assertNull(poolWithoutJmx.getJmxName());
- config.setJmxEnabled(true);
- poolWithoutJmx.jmxUnregister();
- }
-
- config.setJmxNameBase(null);
- try (final GenericObjectPool<String, TestException> poolWithDefaultJmxNameBase = new GenericObjectPool<>(simpleFactory, config)) {
- assertNotNull(poolWithDefaultJmxNameBase.getJmxName());
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testLIFO() throws Exception {
- final String o;
- genericObjectPool.setLifo(true);
- genericObjectPool.addObject(); // "0"
- genericObjectPool.addObject(); // "1"
- genericObjectPool.addObject(); // "2"
- assertEquals( "2", genericObjectPool.borrowObject(),"Youngest");
- assertEquals( "1", genericObjectPool.borrowObject(),"Middle");
- assertEquals( "0", genericObjectPool.borrowObject(),"Oldest");
- o = genericObjectPool.borrowObject();
- assertEquals( "3", o,"new-3");
- genericObjectPool.returnObject(o);
- assertEquals( o, genericObjectPool.borrowObject(),"returned-3");
- assertEquals( "4", genericObjectPool.borrowObject(),"new-4");
- }
-
- /**
- * Test the following scenario:
- * Thread 1 borrows an instance
- * Thread 2 starts to borrow another instance before thread 1 returns its instance
- * Thread 1 returns its instance while thread 2 is validating its newly created instance
- * The test verifies that the instance created by Thread 2 is not leaked.
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMakeConcurrentWithReturn() throws Exception {
- genericObjectPool.setTestOnBorrow(true);
- simpleFactory.setValid(true);
- // Borrow and return an instance, with a short wait
- final WaitingTestThread<TestException> thread1 = new WaitingTestThread<>(genericObjectPool, 200);
- thread1.start();
- Thread.sleep(50); // wait for validation to succeed
- // Slow down validation and borrow an instance
- simpleFactory.setValidateLatency(400);
- final String instance = genericObjectPool.borrowObject();
- // Now make sure that we have not leaked an instance
- assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle() + 1);
- genericObjectPool.returnObject(instance);
- assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxIdle() throws Exception {
- genericObjectPool.setMaxTotal(100);
- genericObjectPool.setMaxIdle(8);
- final String[] active = new String[100];
- for(int i=0;i<100;i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- assertEquals(100,genericObjectPool.getNumActive());
- assertEquals(0,genericObjectPool.getNumIdle());
- for(int i=0;i<100;i++) {
- genericObjectPool.returnObject(active[i]);
- assertEquals(99 - i,genericObjectPool.getNumActive());
- assertEquals((i < 8 ? i+1 : 8),genericObjectPool.getNumIdle());
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxIdleZero() throws Exception {
- genericObjectPool.setMaxTotal(100);
- genericObjectPool.setMaxIdle(0);
- final String[] active = new String[100];
- for(int i=0;i<100;i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- assertEquals(100,genericObjectPool.getNumActive());
- assertEquals(0,genericObjectPool.getNumIdle());
- for(int i=0;i<100;i++) {
- genericObjectPool.returnObject(active[i]);
- assertEquals(99 - i,genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
- }
- }
-
- /**
- * Showcasing a possible deadlock situation as reported in POOL-356
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- @SuppressWarnings("rawtypes")
- public void testMaxIdleZeroUnderLoad() {
- // Config
- final int numThreads = 199; // And main thread makes a round 200.
- final int numIter = 20;
- final int delay = 25;
- final int maxTotal = 10;
-
- simpleFactory.setMaxTotal(maxTotal);
- genericObjectPool.setMaxTotal(maxTotal);
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
-
- // this is important to trigger POOL-356
- genericObjectPool.setMaxIdle(0);
-
- // Start threads to borrow objects
- final TestThread[] threads = new TestThread[numThreads];
- for(int i=0;i<numThreads;i++) {
- // Factor of 2 on iterations so main thread does work whilst other
- // threads are running. Factor of 2 on delay so average delay for
- // other threads == actual delay for main thread
- threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
- final Thread t = new Thread(threads[i]);
- t.start();
- }
- // Give the threads a chance to start doing some work
- Waiter.sleepQuietly(100L);
-
- for (int i = 0; i < numIter; i++) {
- String obj = null;
- try {
- Waiter.sleepQuietly(delay);
- obj = genericObjectPool.borrowObject();
- // Under load, observed numActive > maxTotal
- if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
- throw new IllegalStateException("Too many active objects");
- }
- Waiter.sleepQuietly(delay);
- } catch (final Exception e) {
- // Shouldn't happen
- e.printStackTrace();
- fail("Exception on borrow");
- } finally {
- if (obj != null) {
- try {
- genericObjectPool.returnObject(obj);
- } catch (final Exception e) {
- // Ignore
- }
- }
- }
- }
-
- for (int i = 0; i < numThreads; i++) {
- while (!(threads[i]).complete()) {
- Waiter.sleepQuietly(500L);
- }
- if (threads[i].failed()) {
- threads[i].error.printStackTrace();
- fail("Thread " + i + " failed: " + threads[i].error.toString());
- }
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotal() throws Exception {
- genericObjectPool.setMaxTotal(3);
- genericObjectPool.setBlockWhenExhausted(false);
-
- genericObjectPool.borrowObject();
- genericObjectPool.borrowObject();
- genericObjectPool.borrowObject();
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- }
-
- /**
- * Verifies that maxTotal is not exceeded when factory destroyObject
- * has high latency, testOnReturn is set and there is high incidence of
- * validation failures.
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalInvariant() {
- final int maxTotal = 15;
- simpleFactory.setEvenValid(false); // Every other validation fails
- simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms
- simpleFactory.setMaxTotal(maxTotal); // (makes - destroys) bound
- simpleFactory.setValidationEnabled(true);
- genericObjectPool.setMaxTotal(maxTotal);
- genericObjectPool.setMaxIdle(-1);
- genericObjectPool.setTestOnReturn(true);
- genericObjectPool.setMaxWaitMillis(1000L);
- runTestThreads(5, 10, 50, genericObjectPool);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- @SuppressWarnings("rawtypes")
- public void testMaxTotalUnderLoad() {
- // Config
- final int numThreads = 199; // And main thread makes a round 200.
- final int numIter = 20;
- final int delay = 25;
- final int maxTotal = 10;
-
- simpleFactory.setMaxTotal(maxTotal);
- genericObjectPool.setMaxTotal(maxTotal);
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
-
- // Start threads to borrow objects
- final TestThread[] threads = new TestThread[numThreads];
- for(int i=0;i<numThreads;i++) {
- // Factor of 2 on iterations so main thread does work whilst other
- // threads are running. Factor of 2 on delay so average delay for
- // other threads == actual delay for main thread
- threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
- final Thread t = new Thread(threads[i]);
- t.start();
- }
- // Give the threads a chance to start doing some work
- Waiter.sleepQuietly(5000);
-
- for (int i = 0; i < numIter; i++) {
- String obj = null;
- try {
- Waiter.sleepQuietly(delay);
- obj = genericObjectPool.borrowObject();
- // Under load, observed numActive > maxTotal
- if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
- throw new IllegalStateException("Too many active objects");
- }
- Waiter.sleepQuietly(delay);
- } catch (final Exception e) {
- // Shouldn't happen
- e.printStackTrace();
- fail("Exception on borrow");
- } finally {
- if (obj != null) {
- try {
- genericObjectPool.returnObject(obj);
- } catch (final Exception e) {
- // Ignore
- }
- }
- }
- }
-
- for (int i = 0; i < numThreads; i++) {
- while(!(threads[i]).complete()) {
- Waiter.sleepQuietly(500L);
- }
- if(threads[i].failed()) {
- fail("Thread " + i + " failed: " + threads[i].error.toString());
- }
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalZero() throws Exception {
- genericObjectPool.setMaxTotal(0);
- genericObjectPool.setBlockWhenExhausted(false);
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- }
-
- /*
- * Test multi-threaded pool access.
- * Multiple threads, but maxTotal only allows half the threads to succeed.
- *
- * This test was prompted by Continuum build failures in the Commons DBCP test case:
- * TestPerUserPoolDataSource.testMultipleThreads2()
- * Let's see if the this fails on Continuum too!
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxWaitMultiThreaded() throws Exception {
- final long maxWait = 500; // wait for connection
- final long holdTime = 2 * maxWait; // how long to hold connection
- final int threads = 10; // number of threads to grab the object initially
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setMaxWaitMillis(maxWait);
- genericObjectPool.setMaxTotal(threads);
- // Create enough threads so half the threads will have to wait
- final WaitingTestThread<TestException>[] wtt = new WaitingTestThread[threads * 2];
- for (int i = 0; i < wtt.length; i++) {
- wtt[i] = new WaitingTestThread<>(genericObjectPool, holdTime);
- }
- final long originMillis = System.currentTimeMillis() - 1000;
- for (final WaitingTestThread<TestException> element : wtt) {
- element.start();
- }
- int failed = 0;
- for (final WaitingTestThread<TestException> element : wtt) {
- element.join();
- if (element.thrown != null){
- failed++;
- }
- }
- if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){
- System.out.println(
- "MaxWait: " + maxWait +
- " HoldTime: " + holdTime +
- " MaxTotal: " + threads +
- " Threads: " + wtt.length +
- " Failed: " + failed
- );
- for (final WaitingTestThread<TestException> wt : wtt) {
- System.out.println(
- "PreBorrow: " + (wt.preBorrowMillis - originMillis) +
- " PostBorrow: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) +
- " BorrowTime: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) +
- " PostReturn: " + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) +
- " Ended: " + (wt.endedMillis - originMillis) +
- " ObjId: " + wt.objectId
- );
- }
- }
- assertEquals(wtt.length / 2, failed,"Expected half the threads to fail");
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMinIdle() throws Exception {
- genericObjectPool.setMaxIdle(500);
- genericObjectPool.setMinIdle(5);
- genericObjectPool.setMaxTotal(10);
- genericObjectPool.setNumTestsPerEvictionRun(0);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- genericObjectPool.setTestWhileIdle(true);
-
- Waiter.sleepQuietly(150L);
- assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
-
- final String[] active = new String[5];
- active[0] = genericObjectPool.borrowObject();
-
- Waiter.sleepQuietly(150L);
- assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
-
- for (int i = 1; i < 5; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
-
- Waiter.sleepQuietly(150L);
- assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
-
- for (int i = 0; i < 5; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- Waiter.sleepQuietly(150L);
- assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMinIdleMaxTotal() throws Exception {
- genericObjectPool.setMaxIdle(500);
- genericObjectPool.setMinIdle(5);
- genericObjectPool.setMaxTotal(10);
- genericObjectPool.setNumTestsPerEvictionRun(0);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- genericObjectPool.setTestWhileIdle(true);
-
- Waiter.sleepQuietly(150L);
- assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
-
- final String[] active = new String[10];
-
- Waiter.sleepQuietly(150L);
- assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
-
- for (int i = 0; i < 5; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
-
- Waiter.sleepQuietly(150L);
- assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
-
- for(int i = 0 ; i < 5 ; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- Waiter.sleepQuietly(150L);
- assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
-
- for (int i = 0; i < 10; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
-
- Waiter.sleepQuietly(150L);
- assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle, found " + genericObjectPool.getNumIdle());
-
- for (int i = 0; i < 10; i++) {
- genericObjectPool.returnObject(active[i]);
- }
-
- Waiter.sleepQuietly(150L);
- assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
- }
-
- /**
- * Verifies that returning an object twice (without borrow in between) causes ISE
- * but does not re-validate or re-passivate the instance.
- *
- * JIRA: POOL-285
- */
- @Test
- public void testMultipleReturn() throws Exception {
- final WaiterFactory<String> factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0);
- try (final GenericObjectPool<Waiter, IllegalStateException> pool = new GenericObjectPool<>(factory)) {
- pool.setTestOnReturn(true);
- final Waiter waiter = pool.borrowObject();
- pool.returnObject(waiter);
- assertEquals(1, waiter.getValidationCount());
- assertEquals(1, waiter.getPassivationCount());
- try {
- pool.returnObject(waiter);
- fail("Expecting IllegalStateException from multiple return");
- } catch (final IllegalStateException ex) {
- // Exception is expected, now check no repeat validation/passivation
- assertEquals(1, waiter.getValidationCount());
- assertEquals(1, waiter.getPassivationCount());
- }
- }
- }
-
- // POOL-248
- @Test
- public void testMultipleReturnOfSameObject() throws Exception {
- try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(simpleFactory, new GenericObjectPoolConfig<>())) {
-
- assertEquals(0, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
-
- final String obj = pool.borrowObject();
-
- assertEquals(1, pool.getNumActive());
- assertEquals(0, pool.getNumIdle());
-
- pool.returnObject(obj);
-
- assertEquals(0, pool.getNumActive());
- assertEquals(1, pool.getNumIdle());
-
- assertThrows(IllegalStateException.class,
- () -> pool.returnObject(obj));
-
- assertEquals(0, pool.getNumActive());
- assertEquals(1, pool.getNumIdle());
- }
- }
-
- /**
- * Verifies that when a borrowed object is mutated in a way that does not
- * preserve equality and hashcode, the pool can recognized it on return.
- *
- * JIRA: POOL-284
- */
- @Test
- public void testMutable() throws Exception {
- final HashSetFactory factory = new HashSetFactory();
- try (final GenericObjectPool<HashSet<String>, RuntimeException> pool = new GenericObjectPool<>(factory,
- new GenericObjectPoolConfig<>())) {
- final HashSet<String> s1 = pool.borrowObject();
- final HashSet<String> s2 = pool.borrowObject();
- s1.add("One");
- s2.add("One");
- pool.returnObject(s1);
- pool.returnObject(s2);
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testNegativeMaxTotal() throws Exception {
- genericObjectPool.setMaxTotal(-1);
- genericObjectPool.setBlockWhenExhausted(false);
- final String obj = genericObjectPool.borrowObject();
- assertEquals(getNthObject(0),obj);
- genericObjectPool.returnObject(obj);
- }
-
- /**
- * Verifies that concurrent threads never "share" instances
- */
- @Test
- public void testNoInstanceOverlap() {
- final int maxTotal = 5;
- final int numThreads = 100;
- final int delay = 1;
- final int iterations = 1000;
- final AtomicIntegerFactory factory = new AtomicIntegerFactory();
- try (final GenericObjectPool<AtomicInteger, RuntimeException> pool = new GenericObjectPool<>(factory)) {
- pool.setMaxTotal(maxTotal);
- pool.setMaxIdle(maxTotal);
- pool.setTestOnBorrow(true);
- pool.setBlockWhenExhausted(true);
- pool.setMaxWaitMillis(-1);
- runTestThreads(numThreads, iterations, delay, pool);
- assertEquals(0, pool.getDestroyedByBorrowValidationCount());
- }
- }
-
- /**
- * POOL-376
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testNoInvalidateNPE() throws Exception {
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setTestOnCreate(true);
- genericObjectPool.setMaxWaitMillis(-1);
- final String obj = genericObjectPool.borrowObject();
- // Make validation fail - this will cause create() to return null
- simpleFactory.setValid(false);
- // Create a take waiter
- final WaitingTestThread<TestException> wtt = new WaitingTestThread<>(genericObjectPool, 200);
- wtt.start();
- // Give wtt time to start
- Thread.sleep(200);
- genericObjectPool.invalidateObject(obj);
- // Now allow create to succeed so waiter can be served
- simpleFactory.setValid(true);
- }
-
- public void testPreparePool() throws Exception {
- genericObjectPool.setMinIdle(1);
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.preparePool();
- assertEquals(1, genericObjectPool.getNumIdle());
- final String obj = genericObjectPool.borrowObject();
- genericObjectPool.preparePool();
- assertEquals(0, genericObjectPool.getNumIdle());
- genericObjectPool.setMinIdle(0);
- genericObjectPool.returnObject(obj);
- genericObjectPool.preparePool();
- assertEquals(0, genericObjectPool.getNumIdle());
- }
-
- @Test/* maxWaitMillis x2 + padding */
- @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
- public void testReturnBorrowObjectWithingMaxWaitMillis() throws Exception {
- final long maxWaitMillis = 500;
-
- try (final GenericObjectPool<String, InterruptedException> createSlowObjectFactoryPool = new GenericObjectPool<>(
- createSlowObjectFactory(60000))) {
- createSlowObjectFactoryPool.setMaxTotal(1);
- createSlowObjectFactoryPool.setMaxWaitMillis(maxWaitMillis);
-
- // thread1 tries creating a slow object to make pool full.
- final WaitingTestThread<InterruptedException> thread1 = new WaitingTestThread<>(createSlowObjectFactoryPool, 0);
- thread1.start();
-
- // Wait for thread1's reaching to create().
- Thread.sleep(100);
-
- // another one tries borrowObject. It should return within maxWaitMillis.
- assertThrows(NoSuchElementException.class, () -> createSlowObjectFactoryPool.borrowObject(maxWaitMillis),
- "borrowObject must fail due to timeout by maxWaitMillis");
-
- assertTrue(thread1.isAlive());
- }
- }
-
- /**
- * This is the test case for POOL-263. It is disabled since it will always
- * pass without artificial delay being injected into GOP.returnObject() and
- * a way to this hasn't currently been found that doesn't involve
- * polluting the GOP implementation. The artificial delay needs to be
- * inserted just before the final call to isLifo() in the returnObject()
- * method.
- */
- //@Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testReturnObject() throws Exception {
-
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setMaxIdle(-1);
- final String active = genericObjectPool.borrowObject();
-
- assertEquals(1, genericObjectPool.getNumActive());
- assertEquals(0, genericObjectPool.getNumIdle());
-
- final Thread t = new Thread(() -> genericObjectPool.close());
- t.start();
-
- genericObjectPool.returnObject(active);
-
- // Wait for the close() thread to complete
- while (t.isAlive()) {
- Thread.sleep(50);
- }
-
- assertEquals(0, genericObjectPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testSetConfig() throws Exception {
- final GenericObjectPoolConfig<String> expected = new GenericObjectPoolConfig<>();
- assertConfiguration(expected,genericObjectPool);
- expected.setMaxTotal(2);
- expected.setMaxIdle(3);
- expected.setMaxWait(Duration.ofMillis(5));
- expected.setMinEvictableIdleTime(Duration.ofMillis(7L));
- expected.setNumTestsPerEvictionRun(9);
- expected.setTestOnCreate(true);
- expected.setTestOnBorrow(true);
- expected.setTestOnReturn(true);
- expected.setTestWhileIdle(true);
- expected.setTimeBetweenEvictionRuns(Duration.ofMillis(11L));
- expected.setBlockWhenExhausted(false);
- genericObjectPool.setConfig(expected);
- assertConfiguration(expected,genericObjectPool);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testSettersAndGetters() throws Exception {
- {
- // The object receives an Exception during its creation to prevent
- // memory leaks. See BaseGenericObjectPool constructor for more details.
- assertNotEquals("", genericObjectPool.getCreationStackTrace());
- }
- {
- assertEquals(0, genericObjectPool.getBorrowedCount());
- }
- {
- assertEquals(0, genericObjectPool.getReturnedCount());
- }
- {
- assertEquals(0, genericObjectPool.getCreatedCount());
- }
- {
- assertEquals(0, genericObjectPool.getDestroyedCount());
- }
- {
- assertEquals(0, genericObjectPool.getDestroyedByEvictorCount());
- }
- {
- assertEquals(0, genericObjectPool.getDestroyedByBorrowValidationCount());
- }
- {
- assertEquals(0, genericObjectPool.getMeanActiveTimeMillis());
- }
- {
- assertEquals(0, genericObjectPool.getMeanIdleTimeMillis());
- }
- {
- assertEquals(0, genericObjectPool.getMeanBorrowWaitTimeMillis());
- }
- {
- assertEquals(0, genericObjectPool.getMaxBorrowWaitTimeMillis());
- }
- {
- assertEquals(0, genericObjectPool.getNumIdle());
- }
- {
- genericObjectPool.setMaxTotal(123);
- assertEquals(123,genericObjectPool.getMaxTotal());
- }
- {
- genericObjectPool.setMaxIdle(12);
- assertEquals(12,genericObjectPool.getMaxIdle());
- }
- {
- genericObjectPool.setMaxWaitMillis(1234L);
- assertEquals(1234L,genericObjectPool.getMaxWaitMillis());
- }
- {
- genericObjectPool.setMinEvictableIdleTimeMillis(12345L);
- assertEquals(12345L,genericObjectPool.getMinEvictableIdleDuration().toMillis());
- assertEquals(12345L,genericObjectPool.getMinEvictableIdleTimeMillis());
- assertEquals(12345L,genericObjectPool.getMinEvictableIdleTime().toMillis());
- }
- {
- genericObjectPool.setNumTestsPerEvictionRun(11);
- assertEquals(11,genericObjectPool.getNumTestsPerEvictionRun());
- }
- {
- genericObjectPool.setTestOnBorrow(true);
- assertTrue(genericObjectPool.getTestOnBorrow());
- genericObjectPool.setTestOnBorrow(false);
- assertFalse(genericObjectPool.getTestOnBorrow());
- }
- {
- genericObjectPool.setTestOnReturn(true);
- assertTrue(genericObjectPool.getTestOnReturn());
- genericObjectPool.setTestOnReturn(false);
- assertFalse(genericObjectPool.getTestOnReturn());
- }
- {
- genericObjectPool.setTestWhileIdle(true);
- assertTrue(genericObjectPool.getTestWhileIdle());
- genericObjectPool.setTestWhileIdle(false);
- assertFalse(genericObjectPool.getTestWhileIdle());
- }
- {
- genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L);
- assertEquals(11235L,genericObjectPool.getDurationBetweenEvictionRuns().toMillis());
- assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRunsMillis());
- assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRuns().toMillis());
- }
- {
- genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L);
- assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleDuration().toMillis());
- assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTimeMillis());
- assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTime().toMillis());
- }
- {
- genericObjectPool.setBlockWhenExhausted(true);
- assertTrue(genericObjectPool.getBlockWhenExhausted());
- genericObjectPool.setBlockWhenExhausted(false);
- assertFalse(genericObjectPool.getBlockWhenExhausted());
- }
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testStartAndStopEvictor() throws Exception {
- // set up pool without evictor
- genericObjectPool.setMaxIdle(6);
- genericObjectPool.setMaxTotal(6);
- genericObjectPool.setNumTestsPerEvictionRun(6);
- genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
-
- for (int j = 0; j < 2; j++) {
- // populate the pool
- {
- final String[] active = new String[6];
- for (int i = 0; i < 6; i++) {
- active[i] = genericObjectPool.borrowObject();
- }
- for (int i = 0; i < 6; i++) {
- genericObjectPool.returnObject(active[i]);
- }
- }
-
- // note that it stays populated
- assertEquals(6,genericObjectPool.getNumIdle(),"Should have 6 idle");
-
- // start the evictor
- genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(50));
-
- // wait a second (well, .2 seconds)
- Waiter.sleepQuietly(200L);
-
- // assert that the evictor has cleared out the pool
- assertEquals(0,genericObjectPool.getNumIdle(),"Should have 0 idle");
-
- // stop the evictor
- genericObjectPool.startEvictor(Duration.ZERO);
- }
- }
-
- @Test
- public void testSwallowedExceptionListener() {
- genericObjectPool.setSwallowedExceptionListener(null); // must simply return
- final List<Exception> swallowedExceptions = new ArrayList<>();
- /*
- * A simple listener, that will throw a OOM on 3rd exception.
- */
- final SwallowedExceptionListener listener = e -> {
- if (swallowedExceptions.size() == 2) {
- throw new OutOfMemoryError();
- }
- swallowedExceptions.add(e);
- };
- genericObjectPool.setSwallowedExceptionListener(listener);
-
- final Exception e1 = new Exception();
- final Exception e2 = new ArrayIndexOutOfBoundsException();
-
- genericObjectPool.swallowException(e1);
- genericObjectPool.swallowException(e2);
-
- assertThrows(OutOfMemoryError.class, () -> genericObjectPool.swallowException(e1));
-
- assertEquals(2, swallowedExceptions.size());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testThreaded1() throws Exception {
- genericObjectPool.setMaxTotal(15);
- genericObjectPool.setMaxIdle(15);
- genericObjectPool.setMaxWaitMillis(1000L);
- runTestThreads(20, 100, 50, genericObjectPool);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testTimeoutNoLeak() throws Exception {
- genericObjectPool.setMaxTotal(2);
- genericObjectPool.setMaxWaitMillis(10);
- genericObjectPool.setBlockWhenExhausted(true);
- final String obj = genericObjectPool.borrowObject();
- final String obj2 = genericObjectPool.borrowObject();
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- genericObjectPool.returnObject(obj2);
- genericObjectPool.returnObject(obj);
-
- genericObjectPool.borrowObject();
- genericObjectPool.borrowObject();
- }
-
- /**
- * Tests POOL-361
- */
- @Test
- public void testValidateOnCreate() throws Exception {
- genericObjectPool.setTestOnCreate(true);
- genericObjectPool.addObject();
- assertEquals(1, simpleFactory.validateCounter);
- }
-
- /**
- * Tests POOL-361
- */
- @Test
- public void testValidateOnCreateFailure() throws Exception {
- genericObjectPool.setTestOnCreate(true);
- genericObjectPool.setTestOnBorrow(false);
- genericObjectPool.setMaxTotal(2);
- simpleFactory.setValid(false);
- // Make sure failed validations do not leak capacity
- genericObjectPool.addObject();
- genericObjectPool.addObject();
- assertEquals(0, genericObjectPool.getNumIdle());
- assertEquals(0, genericObjectPool.getNumActive());
- simpleFactory.setValid(true);
- final String obj = genericObjectPool.borrowObject();
- assertNotNull(obj);
- genericObjectPool.addObject();
- // Should have one idle, one out now
- assertEquals(1, genericObjectPool.getNumIdle());
- assertEquals(1, genericObjectPool.getNumActive());
- }
-
- /**
- * Verify that threads waiting on a depleted pool get served when a returning object fails
- * validation.
- *
- * JIRA: POOL-240
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- public void testValidationFailureOnReturnFreesCapacity() throws Exception {
- final SimpleFactory factory = new SimpleFactory();
- factory.setValid(false); // Validate will always fail
- factory.setValidationEnabled(true);
- try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(factory)) {
- pool.setMaxTotal(2);
- pool.setMaxWaitMillis(1500);
- pool.setTestOnReturn(true);
- pool.setTestOnBorrow(false);
- // Borrow an instance and hold if for 5 seconds
- final WaitingTestThread<TestException> thread1 = new WaitingTestThread<>(pool, 5000);
- thread1.start();
- // Borrow another instance and return it after 500 ms (validation will fail)
- final WaitingTestThread<TestException> thread2 = new WaitingTestThread<>(pool, 500);
- thread2.start();
- Thread.sleep(50);
- // Try to borrow an object
- final String obj = pool.borrowObject();
- pool.returnObject(obj);
- }
- }
-
- // POOL-276
- @Test
- public void testValidationOnCreateOnly() throws Exception {
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setTestOnCreate(true);
- genericObjectPool.setTestOnBorrow(false);
- genericObjectPool.setTestOnReturn(false);
- genericObjectPool.setTestWhileIdle(false);
-
- final String o1 = genericObjectPool.borrowObject();
- assertEquals("0", o1);
- final Timer t = new Timer();
- t.schedule(
- new TimerTask() {
- @Override
- public void run() {
- genericObjectPool.returnObject(o1);
- }
- }, 3000);
-
- final String o2 = genericObjectPool.borrowObject();
- assertEquals("0", o2);
-
- assertEquals(1, simpleFactory.validateCounter);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testWhenExhaustedBlock() throws Exception {
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setMaxWaitMillis(10L);
- final String obj1 = genericObjectPool.borrowObject();
- assertNotNull(obj1);
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- genericObjectPool.returnObject(obj1);
- genericObjectPool.close();
- }
-
- /**
- * POOL-189
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testWhenExhaustedBlockClosePool() throws Exception {
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setMaxWaitMillis(-1);
- final Object obj1 = genericObjectPool.borrowObject();
-
- // Make sure an object was obtained
- assertNotNull(obj1);
-
- // Create a separate thread to try and borrow another object
- final WaitingTestThread<TestException> wtt = new WaitingTestThread<TestException>(genericObjectPool, 200);
- wtt.start();
- // Give wtt time to start
- Thread.sleep(200);
-
- // close the pool (Bug POOL-189)
- genericObjectPool.close();
-
- // Give interrupt time to take effect
- Thread.sleep(200);
-
- // Check thread was interrupted
- assertTrue(wtt.thrown instanceof InterruptedException);
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testWhenExhaustedBlockInterrupt() throws Exception {
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setBlockWhenExhausted(true);
- genericObjectPool.setMaxWaitMillis(-1);
- final String obj1 = genericObjectPool.borrowObject();
-
- // Make sure on object was obtained
- assertNotNull(obj1);
-
- // Create a separate thread to try and borrow another object
- final WaitingTestThread<TestException> wtt = new WaitingTestThread<>(genericObjectPool, 200000);
- wtt.start();
- // Give wtt time to start
- Thread.sleep(200);
- wtt.interrupt();
-
- // Give interrupt time to take effect
- Thread.sleep(200);
-
- // Check thread was interrupted
- assertTrue(wtt.thrown instanceof InterruptedException);
-
- // Return object to the pool
- genericObjectPool.returnObject(obj1);
-
- // Bug POOL-162 - check there is now an object in the pool
- genericObjectPool.setMaxWaitMillis(10L);
- String obj2 = null;
- try {
- obj2 = genericObjectPool.borrowObject();
- assertNotNull(obj2);
- } catch (final NoSuchElementException e) {
- // Not expected
- fail("NoSuchElementException not expected");
- }
- genericObjectPool.returnObject(obj2);
- genericObjectPool.close();
-
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testWhenExhaustedFail() throws Exception {
- genericObjectPool.setMaxTotal(1);
- genericObjectPool.setBlockWhenExhausted(false);
- final String obj1 = genericObjectPool.borrowObject();
- assertNotNull(obj1);
- assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
- genericObjectPool.returnObject(obj1);
- assertEquals(1, genericObjectPool.getNumIdle());
- genericObjectPool.close();
- }
-
-}
+/*
+ * 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.impl;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+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;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.lang.management.ManagementFactory;
+import java.lang.ref.WeakReference;
+import java.nio.charset.UnsupportedCharsetException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.commons.pool2.BasePooledObjectFactory;
+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.SwallowedExceptionListener;
+import org.apache.commons.pool2.TestBaseObjectPool;
+import org.apache.commons.pool2.TestException;
+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.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ */
+public class TestGenericObjectPool extends TestBaseObjectPool {
+
+ private class ConcurrentBorrowAndEvictThread extends Thread {
+ private final boolean borrow;
+ public String obj;
+
+ public ConcurrentBorrowAndEvictThread(final boolean borrow) {
+ this.borrow = borrow;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (borrow) {
+ obj = genericObjectPool.borrowObject();
+ } else {
+ genericObjectPool.evict();
+ }
+ } catch (final Exception e) {
+ // Ignore.
+ }
+ }
+ }
+
+ private static class CreateErrorFactory extends BasePooledObjectFactory<String, InterruptedException> {
+
+ private final Semaphore semaphore = new Semaphore(0);
+
+ @Override
+ public String create() throws InterruptedException {
+ semaphore.acquire();
+ throw new UnknownError("wiggle");
+ }
+
+ public boolean hasQueuedThreads() {
+ return semaphore.hasQueuedThreads();
+ }
+
+ public void release() {
+ semaphore.release();
+ }
+
+ @Override
+ public PooledObject<String> wrap(final String obj) {
+ return new DefaultPooledObject<>(obj);
+ }
+ }
+
+ private static class CreateFailFactory extends BasePooledObjectFactory<String, InterruptedException> {
+
+ private final Semaphore semaphore = new Semaphore(0);
+
+ @Override
+ public String create() throws InterruptedException {
+ semaphore.acquire();
+ throw new UnsupportedCharsetException("wibble");
+ }
+
+ public boolean hasQueuedThreads() {
+ return semaphore.hasQueuedThreads();
+ }
+
+ public void release() {
+ semaphore.release();
+ }
+
+ @Override
+ public PooledObject<String> wrap(final String obj) {
+ return new DefaultPooledObject<>(obj);
+ }
+ }
+
+ private static final class DummyFactory
+ extends BasePooledObjectFactory<Object, RuntimeException> {
+ @Override
+ public Object create() {
+ return null;
+ }
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
+ private static class EvictionThread<T, E extends Exception> extends Thread {
+
+ private final GenericObjectPool<T, E> pool;
+
+ public EvictionThread(final GenericObjectPool<T, E> pool) {
+ this.pool = pool;
+ }
+
+ @Override
+ public void run() {
+ try {
+ pool.evict();
+ } catch (final Exception e) {
+ // Ignore
+ }
+ }
+ }
+
+ /**
+ * Factory that creates HashSets. Note that this means
+ * 0) All instances are initially equal (not discernible by equals)
+ * 1) Instances are mutable and mutation can cause change in identity / hashcode.
+ */
+ private static final class HashSetFactory
+ extends BasePooledObjectFactory<HashSet<String>, RuntimeException> {
+ @Override
+ public HashSet<String> create() {
+ return new HashSet<>();
+ }
+ @Override
+ public PooledObject<HashSet<String>> wrap(final HashSet<String> value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
+ /**
+ * Attempts to invalidate an object, swallowing IllegalStateException.
+ */
+ static class InvalidateThread implements Runnable {
+ private final String obj;
+ private final ObjectPool<String, ? extends Exception> pool;
+ private boolean done;
+ public InvalidateThread(final ObjectPool<String, ? extends Exception> pool, final String obj) {
+ this.obj = obj;
+ this.pool = pool;
+ }
+ public boolean complete() {
+ return done;
+ }
+ @Override
+ public void run() {
+ try {
+ pool.invalidateObject(obj);
+ } catch (final IllegalStateException ex) {
+ // Ignore
+ } catch (final Exception ex) {
+ fail("Unexpected exception " + ex.toString());
+ } finally {
+ done = true;
+ }
+ }
+ }
+
+ private static class InvalidFactory
+ extends BasePooledObjectFactory<Object, RuntimeException> {
+
+ @Override
+ public Object create() {
+ return new Object();
+ }
+ @Override
+ public boolean validateObject(final PooledObject<Object> obj) {
+ Waiter.sleepQuietly(1000);
+ return false;
+ }
+
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
+ public static class SimpleFactory implements PooledObjectFactory<String, TestException> {
+ int makeCounter;
+
+ int activationCounter;
+
+ int validateCounter;
+
+ int activeCount;
+
+ boolean evenValid = true;
+
+ boolean oddValid = true;
+
+ boolean exceptionOnPassivate;
+
+ boolean exceptionOnActivate;
+
+ boolean exceptionOnDestroy;
+
+ boolean exceptionOnValidate;
+
+ boolean enableValidation = true;
+
+ long destroyLatency;
+
+ long makeLatency;
+
+ long validateLatency;
+
+ int maxTotal = Integer.MAX_VALUE;
+
+ public SimpleFactory() {
+ this(true);
+ }
+
+ public SimpleFactory(final boolean valid) {
+ this(valid,valid);
+ }
+
+ public SimpleFactory(final boolean evalid, final boolean ovalid) {
+ evenValid = evalid;
+ oddValid = ovalid;
+ }
+
+ @Override
+ public void activateObject(final PooledObject<String> obj) throws TestException {
+ final boolean hurl;
+ final boolean evenTest;
+ final boolean oddTest;
+ final int counter;
+ synchronized(this) {
+ hurl = exceptionOnActivate;
+ evenTest = evenValid;
+ oddTest = oddValid;
+ counter = activationCounter++;
+ }
+ if (hurl && !(counter%2 == 0 ? evenTest : oddTest)) {
+ throw new TestException();
+ }
+ }
+
+ @Override
+ public void destroyObject(final PooledObject<String> obj) throws TestException {
+ final long waitLatency;
+ final boolean hurl;
+ synchronized(this) {
+ waitLatency = destroyLatency;
+ hurl = exceptionOnDestroy;
+ }
+ if (waitLatency > 0) {
+ doWait(waitLatency);
+ }
+ synchronized(this) {
+ activeCount--;
+ }
+ if (hurl) {
+ throw new TestException();
+ }
+ }
+
+ private void doWait(final long latency) {
+ Waiter.sleepQuietly(latency);
+ }
+
+ public synchronized int getMakeCounter() {
+ return makeCounter;
+ }
+
+ public synchronized boolean isThrowExceptionOnActivate() {
+ return exceptionOnActivate;
+ }
+
+ public synchronized boolean isValidationEnabled() {
+ return enableValidation;
+ }
+
+ @Override
+ public PooledObject<String> makeObject() {
+ final long waitLatency;
+ synchronized(this) {
+ activeCount++;
+ if (activeCount > maxTotal) {
+ throw new IllegalStateException(
+ "Too many active instances: " + activeCount);
+ }
+ waitLatency = makeLatency;
+ }
+ if (waitLatency > 0) {
+ doWait(waitLatency);
+ }
+ final int counter;
+ synchronized(this) {
+ counter = makeCounter++;
+ }
+ return new DefaultPooledObject<>(String.valueOf(counter));
+ }
+
+ @Override
+ public void passivateObject(final PooledObject<String> obj) throws TestException {
+ final boolean hurl;
+ synchronized(this) {
+ hurl = exceptionOnPassivate;
+ }
+ if (hurl) {
+ throw new TestException();
+ }
+ }
+
+ public synchronized void setDestroyLatency(final long destroyLatency) {
+ this.destroyLatency = destroyLatency;
+ }
+
+ public synchronized void setEvenValid(final boolean valid) {
+ evenValid = valid;
+ }
+
+ public synchronized void setMakeLatency(final long makeLatency) {
+ this.makeLatency = makeLatency;
+ }
+
+ public synchronized void setMaxTotal(final int maxTotal) {
+ this.maxTotal = maxTotal;
+ }
+
+ public synchronized void setOddValid(final boolean valid) {
+ oddValid = valid;
+ }
+
+ public synchronized void setThrowExceptionOnActivate(final boolean b) {
+ exceptionOnActivate = b;
+ }
+
+ public synchronized void setThrowExceptionOnDestroy(final boolean b) {
+ exceptionOnDestroy = b;
+ }
+
+ public synchronized void setThrowExceptionOnPassivate(final boolean bool) {
+ exceptionOnPassivate = bool;
+ }
+
+ public synchronized void setThrowExceptionOnValidate(final boolean bool) {
+ exceptionOnValidate = bool;
+ }
+
+ public synchronized void setValid(final boolean valid) {
+ setEvenValid(valid);
+ setOddValid(valid);
+ }
+
+ public synchronized void setValidateLatency(final long validateLatency) {
+ this.validateLatency = validateLatency;
+ }
+
+ public synchronized void setValidationEnabled(final boolean b) {
+ enableValidation = b;
+ }
+
+ @Override
+ public boolean validateObject(final PooledObject<String> obj) {
+ final boolean validate;
+ final boolean throwException;
+ final boolean evenTest;
+ final boolean oddTest;
+ final long waitLatency;
+ final int counter;
+ synchronized(this) {
+ validate = enableValidation;
+ throwException = exceptionOnValidate;
+ evenTest = evenValid;
+ oddTest = oddValid;
+ counter = validateCounter++;
+ waitLatency = validateLatency;
+ }
+ if (waitLatency > 0) {
+ doWait(waitLatency);
+ }
+ if (throwException) {
+ throw new RuntimeException("validation failed");
+ }
+ if (validate) {
+ return counter%2 == 0 ? evenTest : oddTest;
+ }
+ return true;
+ }
+ }
+
+ public static class TestEvictionPolicy<T> implements EvictionPolicy<T> {
+
+ private final AtomicInteger callCount = new AtomicInteger(0);
+
+ @Override
+ public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
+ final int idleCount) {
+ return callCount.incrementAndGet() > 1500;
+ }
+ }
+
+ static class TestThread<T, E extends Exception> implements Runnable {
+
+ /** source of random delay times */
+ private final java.util.Random random;
+
+ /** pool to borrow from */
+ private final ObjectPool<T, E> pool;
+
+ /** number of borrow attempts */
+ private final int iter;
+
+ /** delay before each borrow attempt */
+ private final int startDelay;
+
+ /** time to hold each borrowed object before returning it */
+ private final int holdTime;
+
+ /** whether or not start and hold time are randomly generated */
+ private final boolean randomDelay;
+
+ /** object expected to be borrowed (fail otherwise) */
+ private final Object expectedObject;
+
+ private volatile boolean complete;
+ private volatile boolean failed;
+ private volatile Throwable error;
+
+ public TestThread(final ObjectPool<T, E> pool) {
+ this(pool, 100, 50, true, null);
+ }
+
+ public TestThread(final ObjectPool<T, E> pool, final int iter) {
+ this(pool, iter, 50, true, null);
+ }
+
+ public TestThread(final ObjectPool<T, E> pool, final int iter, final int delay) {
+ this(pool, iter, delay, true, null);
+ }
+
+ public TestThread(final ObjectPool<T, E> pool, final int iter, final int delay,
+ final boolean randomDelay) {
+ this(pool, iter, delay, randomDelay, null);
+ }
+
+ public TestThread(final ObjectPool<T, E> pool, final int iter, final int delay,
+ final boolean randomDelay, final Object obj) {
+ this(pool, iter, delay, delay, randomDelay, obj);
+ }
+
+ public TestThread(final ObjectPool<T, E> pool, final int iter, final int startDelay,
+ final int holdTime, final boolean randomDelay, final Object obj) {
+ this.pool = pool;
+ this.iter = iter;
+ this.startDelay = startDelay;
+ this.holdTime = holdTime;
+ this.randomDelay = randomDelay;
+ this.random = this.randomDelay ? new Random() : null;
+ this.expectedObject = obj;
+ }
+
+ public boolean complete() {
+ return complete;
+ }
+
+ public boolean failed() {
+ return failed;
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < iter; i++) {
+ final long actualStartDelay = randomDelay ? (long) random.nextInt(startDelay) : startDelay;
+ final long actualHoldTime = randomDelay ? (long) random.nextInt(holdTime) : holdTime;
+ Waiter.sleepQuietly(actualStartDelay);
+ T obj = null;
+ try {
+ obj = pool.borrowObject();
+ } catch (final Exception e) {
+ error = e;
+ failed = true;
+ complete = true;
+ break;
+ }
+
+ if (expectedObject != null && !expectedObject.equals(obj)) {
+ error = new Throwable("Expected: " + expectedObject + " found: " + obj);
+ failed = true;
+ complete = true;
+ break;
+ }
+
+ Waiter.sleepQuietly(actualHoldTime);
+ try {
+ pool.returnObject(obj);
+ } catch (final Exception e) {
+ error = e;
+ failed = true;
+ complete = true;
+ break;
+ }
+ }
+ complete = true;
+ }
+ }
+
+ /*
+ * Very simple test thread that just tries to borrow an object from
+ * the provided pool returns it after a wait
+ */
+ static class WaitingTestThread<E extends Exception> extends Thread {
+ private final GenericObjectPool<String, E> pool;
+ private final long pause;
+ private Throwable thrown;
+
+ private long preBorrowMillis; // just before borrow
+ private long postBorrowMillis; // borrow returned
+ private long postReturnMillis; // after object was returned
+ private long endedMillis;
+ private String objectId;
+
+ public WaitingTestThread(final GenericObjectPool<String, E> pool, final long pause) {
+ this.pool = pool;
+ this.pause = pause;
+ this.thrown = null;
+ }
+
+ @Override
+ public void run() {
+ try {
+ preBorrowMillis = System.currentTimeMillis();
+ final String obj = pool.borrowObject();
+ objectId = obj;
+ postBorrowMillis = System.currentTimeMillis();
+ Thread.sleep(pause);
+ pool.returnObject(obj);
+ postReturnMillis = System.currentTimeMillis();
+ } catch (final Throwable e) {
+ thrown = e;
+ } finally{
+ endedMillis = System.currentTimeMillis();
+ }
+ }
+ }
+
+ private static final boolean DISPLAY_THREAD_DETAILS=
+ Boolean.getBoolean("TestGenericObjectPool.display.thread.details");
+ // To pass this to a Maven test, use:
+ // mvn test -DargLine="-DTestGenericObjectPool.display.thread.details=true"
+ // @see https://issues.apache.org/jira/browse/SUREFIRE-121
+
+ protected GenericObjectPool<String, TestException> genericObjectPool;
+
+ private SimpleFactory simpleFactory;
+
+ @SuppressWarnings("deprecation")
+ private void assertConfiguration(final GenericObjectPoolConfig<?> expected, final GenericObjectPool<?, ?> actual) {
+ assertEquals(Boolean.valueOf(expected.getTestOnCreate()), Boolean.valueOf(actual.getTestOnCreate()),
+ "testOnCreate");
+ assertEquals(Boolean.valueOf(expected.getTestOnBorrow()), Boolean.valueOf(actual.getTestOnBorrow()),
+ "testOnBorrow");
+ assertEquals(Boolean.valueOf(expected.getTestOnReturn()), Boolean.valueOf(actual.getTestOnReturn()),
+ "testOnReturn");
+ assertEquals(Boolean.valueOf(expected.getTestWhileIdle()), Boolean.valueOf(actual.getTestWhileIdle()),
+ "testWhileIdle");
+ assertEquals(Boolean.valueOf(expected.getBlockWhenExhausted()), Boolean.valueOf(actual.getBlockWhenExhausted()),
+ "whenExhaustedAction");
+ assertEquals(expected.getMaxTotal(), actual.getMaxTotal(), "maxTotal");
+ assertEquals(expected.getMaxIdle(), actual.getMaxIdle(), "maxIdle");
+ assertEquals(expected.getMaxWaitMillis(), actual.getMaxWaitMillis(), "maxWaitDuration");
+ assertEquals(expected.getMaxWaitDuration(), actual.getMaxWaitDuration(), "maxWaitDuration");
+ assertEquals(expected.getMinEvictableIdleTimeMillis(), actual.getMinEvictableIdleTimeMillis(),
+ "minEvictableIdleTimeMillis");
+ assertEquals(expected.getMinEvictableIdleTime(), actual.getMinEvictableIdleTime(),
+ "minEvictableIdleTime");
+ assertEquals(expected.getMinEvictableIdleDuration(), actual.getMinEvictableIdleDuration(),
+ "minEvictableIdleDuration");
+ assertEquals(expected.getNumTestsPerEvictionRun(), actual.getNumTestsPerEvictionRun(),
+ "numTestsPerEvictionRun");
+ assertEquals(expected.getEvictorShutdownTimeoutDuration(), actual.getEvictorShutdownTimeoutDuration(),
+ "evictorShutdownTimeoutDuration");
+ assertEquals(expected.getEvictorShutdownTimeoutMillis(), actual.getEvictorShutdownTimeoutMillis(),
+ "evictorShutdownTimeoutMillis");
+ assertEquals(expected.getEvictorShutdownTimeout(), actual.getEvictorShutdownTimeout(),
+ "evictorShutdownTimeout");
+ assertEquals(expected.getTimeBetweenEvictionRunsMillis(), actual.getTimeBetweenEvictionRunsMillis(),
+ "timeBetweenEvictionRunsMillis");
+ assertEquals(expected.getDurationBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(),
+ "timeBetweenEvictionRuns");
+ assertEquals(expected.getTimeBetweenEvictionRuns(), actual.getTimeBetweenEvictionRuns(),
+ "timeBetweenEvictionRuns");
+ }
+
+ private void checkEvict(final boolean lifo) throws Exception {
+ // yea this is hairy but it tests all the code paths in GOP.evict()
+ genericObjectPool.setSoftMinEvictableIdle(Duration.ofMillis(10));
+ genericObjectPool.setSoftMinEvictableIdleTime(Duration.ofMillis(10));
+ genericObjectPool.setMinIdle(2);
+ genericObjectPool.setTestWhileIdle(true);
+ genericObjectPool.setLifo(lifo);
+ genericObjectPool.addObjects(5);
+ genericObjectPool.evict();
+ simpleFactory.setEvenValid(false);
+ simpleFactory.setOddValid(false);
+ simpleFactory.setThrowExceptionOnActivate(true);
+ genericObjectPool.evict();
+ genericObjectPool.addObjects(5);
+ simpleFactory.setThrowExceptionOnActivate(false);
+ simpleFactory.setThrowExceptionOnPassivate(true);
+ genericObjectPool.evict();
+ simpleFactory.setThrowExceptionOnPassivate(false);
+ simpleFactory.setEvenValid(true);
+ simpleFactory.setOddValid(true);
+ Thread.sleep(125);
+ genericObjectPool.evict();
+ assertEquals(2, genericObjectPool.getNumIdle());
+ }
+
+ private void checkEvictionOrder(final boolean lifo) throws Exception {
+ checkEvictionOrderPart1(lifo);
+ tearDown();
+ setUp();
+ checkEvictionOrderPart2(lifo);
+ }
+
+ private void checkEvictionOrderPart1(final boolean lifo) throws Exception {
+ genericObjectPool.setNumTestsPerEvictionRun(2);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
+ genericObjectPool.setLifo(lifo);
+ for (int i = 0; i < 5; i++) {
+ genericObjectPool.addObject();
+ Thread.sleep(100);
+ }
+ // Order, oldest to youngest, is "0", "1", ...,"4"
+ genericObjectPool.evict(); // Should evict "0" and "1"
+ final Object obj = genericObjectPool.borrowObject();
+ assertNotEquals("0", obj, "oldest not evicted");
+ assertNotEquals("1", obj, "second oldest not evicted");
+ // 2 should be next out for FIFO, 4 for LIFO
+ assertEquals(lifo ? "4" : "2" , obj,"Wrong instance returned");
+ }
+
+ private void checkEvictionOrderPart2(final boolean lifo) throws Exception {
+ // Two eviction runs in sequence
+ genericObjectPool.setNumTestsPerEvictionRun(2);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
+ genericObjectPool.setLifo(lifo);
+ for (int i = 0; i < 5; i++) {
+ genericObjectPool.addObject();
+ Thread.sleep(100);
+ }
+ genericObjectPool.evict(); // Should evict "0" and "1"
+ genericObjectPool.evict(); // Should evict "2" and "3"
+ final Object obj = genericObjectPool.borrowObject();
+ assertEquals("4", obj,"Wrong instance remaining in pool");
+ }
+
+ private void checkEvictorVisiting(final boolean lifo) throws Exception {
+ VisitTracker<Object> obj;
+ VisitTrackerFactory<Object> trackerFactory = new VisitTrackerFactory<>();
+ try (GenericObjectPool<VisitTracker<Object>,RuntimeException> trackerPool = new GenericObjectPool<>(trackerFactory)) {
+ trackerPool.setNumTestsPerEvictionRun(2);
+ trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
+ trackerPool.setTestWhileIdle(true);
+ trackerPool.setLifo(lifo);
+ trackerPool.setTestOnReturn(false);
+ trackerPool.setTestOnBorrow(false);
+ for (int i = 0; i < 8; i++) {
+ trackerPool.addObject();
+ }
+ trackerPool.evict(); // Visit oldest 2 - 0 and 1
+ obj = trackerPool.borrowObject();
+ trackerPool.returnObject(obj);
+ obj = trackerPool.borrowObject();
+ trackerPool.returnObject(obj);
+ // borrow, return, borrow, return
+ // FIFO will move 0 and 1 to end
+ // LIFO, 7 out, then in, then out, then in
+ trackerPool.evict(); // Should visit 2 and 3 in either case
+ for (int i = 0; i < 8; i++) {
+ final VisitTracker<Object> tracker = trackerPool.borrowObject();
+ if (tracker.getId() >= 4) {
+ assertEquals( 0, tracker.getValidateCount(),"Unexpected instance visited " + tracker.getId());
+ } else {
+ assertEquals( 1, tracker.getValidateCount(),
+ "Instance " + tracker.getId() + " visited wrong number of times.");
+ }
+ }
+ }
+
+ trackerFactory = new VisitTrackerFactory<>();
+ try (GenericObjectPool<VisitTracker<Object>, RuntimeException> trackerPool = new GenericObjectPool<>(trackerFactory)) {
+ trackerPool.setNumTestsPerEvictionRun(3);
+ trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
+ trackerPool.setTestWhileIdle(true);
+ trackerPool.setLifo(lifo);
+ trackerPool.setTestOnReturn(false);
+ trackerPool.setTestOnBorrow(false);
+ for (int i = 0; i < 8; i++) {
+ trackerPool.addObject();
+ }
+ trackerPool.evict(); // 0, 1, 2
+ trackerPool.evict(); // 3, 4, 5
+ obj = trackerPool.borrowObject();
+ trackerPool.returnObject(obj);
+ obj = trackerPool.borrowObject();
+ trackerPool.returnObject(obj);
+ obj = trackerPool.borrowObject();
+ trackerPool.returnObject(obj);
+ // borrow, return, borrow, return
+ // FIFO 3,4,5,6,7,0,1,2
+ // LIFO 7,6,5,4,3,2,1,0
+ // In either case, pointer should be at 6
+ trackerPool.evict();
+ // Should hit 6,7,0 - 0 for second time
+ for (int i = 0; i < 8; i++) {
+ final VisitTracker<Object> tracker = trackerPool.borrowObject();
+ if (tracker.getId() != 0) {
+ assertEquals( 1, tracker.getValidateCount(),
+ "Instance " + tracker.getId() + " visited wrong number of times.");
+ } else {
+ assertEquals( 2, tracker.getValidateCount(),
+ "Instance " + tracker.getId() + " visited wrong number of times.");
+ }
+ }
+ }
+
+ // Randomly generate a pools with random numTests
+ // and make sure evictor cycles through elements appropriately
+ final int[] smallPrimes = { 2, 3, 5, 7 };
+ final Random random = new Random();
+ random.setSeed(System.currentTimeMillis());
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 5; j++) {
+ try (GenericObjectPool<VisitTracker<Object>, RuntimeException> trackerPool = new GenericObjectPool<>(trackerFactory)) {
+ trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]);
+ trackerPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
+ trackerPool.setTestWhileIdle(true);
+ trackerPool.setLifo(lifo);
+ trackerPool.setTestOnReturn(false);
+ trackerPool.setTestOnBorrow(false);
+ trackerPool.setMaxIdle(-1);
+ final int instanceCount = 10 + random.nextInt(20);
+ trackerPool.setMaxTotal(instanceCount);
+ for (int k = 0; k < instanceCount; k++) {
+ trackerPool.addObject();
+ }
+
+ // Execute a random number of evictor runs
+ final int runs = 10 + random.nextInt(50);
+ for (int k = 0; k < runs; k++) {
+ trackerPool.evict();
+ }
+
+ // Number of times evictor should have cycled through the pool
+ final int cycleCount = runs * trackerPool.getNumTestsPerEvictionRun() / instanceCount;
+
+ // Look at elements and make sure they are visited cycleCount
+ // or cycleCount + 1 times
+ VisitTracker<Object> tracker = null;
+ int visitCount = 0;
+ for (int k = 0; k < instanceCount; k++) {
+ tracker = trackerPool.borrowObject();
+ assertTrue(trackerPool.getNumActive() <= trackerPool.getMaxTotal());
+ visitCount = tracker.getValidateCount();
+ assertTrue(visitCount >= cycleCount && visitCount <= cycleCount + 1);
+ }
+ }
+ }
+ }
+ }
+
+ private BasePooledObjectFactory<String, RuntimeException> createDefaultPooledObjectFactory() {
+ return new BasePooledObjectFactory<String, RuntimeException>() {
+ @Override
+ public String create() {
+ // fake
+ return null;
+ }
+
+ @Override
+ public PooledObject<String> wrap(final String obj) {
+ // fake
+ return new DefaultPooledObject<>(obj);
+ }
+ };
+ }
+
+ private BasePooledObjectFactory<String, RuntimeException> createNullPooledObjectFactory() {
+ return new BasePooledObjectFactory<String, RuntimeException>() {
+ @Override
+ public String create() {
+ // fake
+ return null;
+ }
+
+ @Override
+ public PooledObject<String> wrap(final String obj) {
+ // fake
+ return null;
+ }
+ };
+ }
+
+ private BasePooledObjectFactory<String, InterruptedException> createSlowObjectFactory(final long elapsedTimeMillis) {
+ return new BasePooledObjectFactory<String, InterruptedException>() {
+ @Override
+ public String create() throws InterruptedException {
+ Thread.sleep(elapsedTimeMillis);
+ return "created";
+ }
+
+ @Override
+ public PooledObject<String> wrap(final String obj) {
+ // fake
+ return new DefaultPooledObject<>(obj);
+ }
+ };
+ }
+
+ @Override
+ protected Object getNthObject(final int n) {
+ return String.valueOf(n);
+ }
+
+ @Override
+ protected boolean isFifo() {
+ return false;
+ }
+
+ @Override
+ protected boolean isLifo() {
+ return true;
+ }
+
+ @Override
+ protected ObjectPool<String, TestException> makeEmptyPool(final int minCap) {
+ final GenericObjectPool<String, TestException> mtPool = new GenericObjectPool<>(new SimpleFactory());
+ mtPool.setMaxTotal(minCap);
+ mtPool.setMaxIdle(minCap);
+ return mtPool;
+ }
+
+ @Override
+ protected <E extends Exception> ObjectPool<Object, E> makeEmptyPool(final PooledObjectFactory<Object, E> fac) {
+ return new GenericObjectPool<>(fac);
+ }
+
+ /**
+ * Kicks off <numThreads> test threads, each of which will go through
+ * <iterations> borrow-return cycles with random delay times <= delay
+ * in between.
+ */
+ private <T, E extends Exception> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericObjectPool<T, E> testPool) {
+ final TestThread<T, E>[] threads = new TestThread[numThreads];
+ for (int i = 0; i < numThreads; i++) {
+ threads[i] = new TestThread<>(testPool, iterations, delay);
+ final Thread t = new Thread(threads[i]);
+ t.start();
+ }
+ for (int i = 0; i < numThreads; i++) {
+ while (!threads[i].complete()) {
+ Waiter.sleepQuietly(500L);
+ }
+ if (threads[i].failed()) {
+ fail("Thread " + i + " failed: " + threads[i].error.toString());
+ }
+ }
+ }
+
+ @BeforeEach
+ public void setUp() {
+ simpleFactory = new SimpleFactory();
+ genericObjectPool = new GenericObjectPool<>(simpleFactory);
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ final ObjectName jmxName = genericObjectPool.getJmxName();
+ final String poolName = Objects.toString(jmxName, null);
+
+ genericObjectPool.clear();
+ genericObjectPool.close();
+ genericObjectPool = null;
+ simpleFactory = null;
+
+ final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ final Set<ObjectName> result = mbs.queryNames(new ObjectName("org.apache.commoms.pool2:type=GenericObjectPool,*"), null);
+ // There should be no registered pools at this point
+ final int registeredPoolCount = result.size();
+ final StringBuilder msg = new StringBuilder("Current pool is: ");
+ msg.append(poolName);
+ msg.append(" Still open pools are: ");
+ for (final ObjectName name : result) {
+ // Clean these up ready for the next test
+ msg.append(name.toString());
+ msg.append(" created via\n");
+ msg.append(mbs.getAttribute(name, "CreationStackTrace"));
+ msg.append('\n');
+ mbs.unregisterMBean(name);
+ }
+ assertEquals(0, registeredPoolCount, msg.toString());
+
+ // Make sure that EvictionTimer executor is shut down.
+ Thread.yield();
+ if (EvictionTimer.getExecutor() != null) {
+ Thread.sleep(1000);
+ }
+ assertNull(EvictionTimer.getExecutor(), "EvictionTimer.getExecutor()");
+ }
+
+ /**
+ * Check that a pool that starts an evictor, but is never closed does not leave EvictionTimer executor running. Confirmation check is in
+ * {@link #tearDown()}.
+ *
+ * @throws TestException Custom exception
+ * @throws InterruptedException if any thread has interrupted the current thread. The <em>interrupted status</em> of the current thread is cleared when this
+ * exception is thrown.
+ */
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testAbandonedPool() throws TestException, InterruptedException {
+ final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
+ config.setJmxEnabled(false);
+ GenericObjectPool<String, TestException> abandoned = new GenericObjectPool<>(simpleFactory, config);
+ abandoned.setTimeBetweenEvictionRuns(Duration.ofMillis(100)); // Starts evictor
+ assertEquals(abandoned.getRemoveAbandonedTimeout(), abandoned.getRemoveAbandonedTimeoutDuration().getSeconds());
+
+ // This is ugly, but forces GC to hit the pool
+ final WeakReference<GenericObjectPool<String, TestException>> ref = new WeakReference<>(abandoned);
+ abandoned = null;
+ while (ref.get() != null) {
+ System.gc();
+ Thread.sleep(100);
+ }
+ }
+
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testAddObject() throws Exception {
+ assertEquals( 0, genericObjectPool.getNumIdle(),"should be zero idle");
+ genericObjectPool.addObject();
+ assertEquals( 1, genericObjectPool.getNumIdle(),"should be one idle");
+ assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active");
+ final String obj = genericObjectPool.borrowObject();
+ assertEquals( 0, genericObjectPool.getNumIdle(),"should be zero idle");
+ assertEquals( 1, genericObjectPool.getNumActive(),"should be one active");
+ genericObjectPool.returnObject(obj);
+ assertEquals( 1, genericObjectPool.getNumIdle(),"should be one idle");
+ assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active");
+ }
+
+ @Test
+ public void testAppendStats() {
+ assertFalse(genericObjectPool.getMessageStatistics());
+ assertEquals("foo", genericObjectPool.appendStats("foo"));
+ try (final GenericObjectPool<?, TestException> 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
+ * aware of this if you see a failure of this test.
+ */
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testBorrowObjectFairness() throws Exception {
+
+ final int numThreads = 40;
+ final int maxTotal = 40;
+
+ final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
+ config.setMaxTotal(maxTotal);
+ config.setMaxIdle(maxTotal);
+ config.setFairness(true);
+ config.setLifo(false);
+
+ genericObjectPool = new GenericObjectPool(simpleFactory, config);
+
+ // Exhaust the pool
+ final String[] objects = new String[maxTotal];
+ for (int i = 0; i < maxTotal; i++) {
+ objects[i] = genericObjectPool.borrowObject();
+ }
+
+ // Start and park threads waiting to borrow objects
+ final TestThread[] threads = new TestThread[numThreads];
+ for(int i=0;i<numThreads;i++) {
+ threads[i] = new TestThread(genericObjectPool, 1, 0, 2000, false, String.valueOf(i % maxTotal));
+ final Thread t = new Thread(threads[i]);
+ t.start();
+ // Short delay to ensure threads start in correct order
+ try {
+ Thread.sleep(10);
+ } catch (final InterruptedException e) {
+ fail(e.toString());
+ }
+ }
+
+ // Return objects, other threads should get served in order
+ for (int i = 0; i < maxTotal; i++) {
+ genericObjectPool.returnObject(objects[i]);
+ }
+
+ // Wait for threads to finish
+ for (int i = 0; i < numThreads; i++) {
+ while (!threads[i].complete()) {
+ Waiter.sleepQuietly(500L);
+ }
+ if (threads[i].failed()) {
+ fail("Thread " + i + " failed: " + threads[i].error.toString());
+ }
+ }
+ }
+
+ @Test
+ public void testBorrowTimings() throws Exception {
+ // Borrow
+ final String object = genericObjectPool.borrowObject();
+ final PooledObject<String> po = genericObjectPool.getPooledObject(object);
+ // In the initial state, all instants are the creation instant: last borrow, last use, last return.
+ // In the initial state, the active duration is the time between "now" and the creation time.
+ // In the initial state, the idle duration is the time between "now" and the last return, which is the creation time.
+ // But... this PO might have already been used in other tests in this class.
+
+ final Instant lastBorrowInstant1 = po.getLastBorrowInstant();
+ final Instant lastReturnInstant1 = po.getLastReturnInstant();
+ final Instant lastUsedInstant1 = po.getLastUsedInstant();
+
+ assertThat(po.getCreateInstant(), lessThanOrEqualTo(lastBorrowInstant1));
+ assertThat(po.getCreateInstant(), lessThanOrEqualTo(lastReturnInstant1));
+ assertThat(po.getCreateInstant(), lessThanOrEqualTo(lastUsedInstant1));
+ assertThat(po.getCreateTime(), lessThanOrEqualTo(lastBorrowInstant1.toEpochMilli()));
+ assertThat(po.getCreateTime(), lessThanOrEqualTo(lastReturnInstant1.toEpochMilli()));
+ assertThat(po.getCreateTime(), lessThanOrEqualTo(lastUsedInstant1.toEpochMilli()));
+
+ // Sleep MUST be "long enough" to detect that more than 0 milliseconds have elapsed.
+ // Need an API in Java 8 to get the clock granularity.
+ Thread.sleep(200);
+
+ assertFalse(po.getActiveDuration().isNegative());
+ assertFalse(po.getActiveDuration().isZero());
+ // We use greaterThanOrEqualTo instead of equal because "now" many be different when each argument is evaluated.
+ assertThat(1L, lessThanOrEqualTo(2L)); // sanity check
+ assertThat(Duration.ZERO, lessThanOrEqualTo(Duration.ZERO.plusNanos(1))); // sanity check
+ assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getIdleDuration()));
+ // Deprecated
+ assertThat(po.getActiveDuration().toMillis(), lessThanOrEqualTo(po.getActiveTimeMillis()));
+ assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getActiveTime()));
+ //
+ // TODO How to compare ID with AD since other tests may have touched the PO?
+ assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getIdleTime()));
+ assertThat(po.getActiveDuration().toMillis(), lessThanOrEqualTo(po.getIdleTimeMillis()));
+ //
+ assertThat(po.getCreateInstant(), lessThanOrEqualTo(po.getLastBorrowInstant()));
+ assertThat(po.getCreateInstant(), lessThanOrEqualTo(po.getLastReturnInstant()));
+ assertThat(po.getCreateInstant(), lessThanOrEqualTo(po.getLastUsedInstant()));
+
+ assertThat(lastBorrowInstant1, lessThanOrEqualTo(po.getLastBorrowInstant()));
+ assertThat(lastReturnInstant1, lessThanOrEqualTo(po.getLastReturnInstant()));
+ assertThat(lastUsedInstant1, lessThanOrEqualTo(po.getLastUsedInstant()));
+
+ genericObjectPool.returnObject(object);
+
+ assertFalse(po.getActiveDuration().isNegative());
+ assertFalse(po.getActiveDuration().isZero());
+ assertThat(po.getActiveDuration().toMillis(), lessThanOrEqualTo(po.getActiveTimeMillis()));
+ assertThat(po.getActiveDuration(), lessThanOrEqualTo(po.getActiveTime()));
+
+ assertThat(lastBorrowInstant1, lessThanOrEqualTo(po.getLastBorrowInstant()));
+ assertThat(lastReturnInstant1, lessThanOrEqualTo(po.getLastReturnInstant()));
+ assertThat(lastUsedInstant1, lessThanOrEqualTo(po.getLastUsedInstant()));
+ }
+
+ /**
+ * On first borrow, first object fails validation, second object is OK.
+ * Subsequent borrows are OK. This was POOL-152.
+ * @throws Exception
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testBrokenFactoryShouldNotBlockPool() throws Exception {
+ final int maxTotal = 1;
+
+ simpleFactory.setMaxTotal(maxTotal);
+ genericObjectPool.setMaxTotal(maxTotal);
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setTestOnBorrow(true);
+
+ // First borrow object will need to create a new object which will fail
+ // validation.
+ String obj = null;
+ Exception ex = null;
+ simpleFactory.setValid(false);
+ try {
+ obj = genericObjectPool.borrowObject();
+ } catch (final Exception e) {
+ ex = e;
+ }
+ // Failure expected
+ assertNotNull(ex);
+ assertTrue(ex instanceof NoSuchElementException);
+ assertNull(obj);
+
+ // Configure factory to create valid objects so subsequent borrows work
+ simpleFactory.setValid(true);
+
+ // Subsequent borrows should be OK
+ obj = genericObjectPool.borrowObject();
+ assertNotNull(obj);
+ genericObjectPool.returnObject(obj);
+ }
+
+ // POOL-259
+ @Test
+ public void testClientWaitStats() throws TestException {
+ final SimpleFactory factory = new SimpleFactory();
+ // Give makeObject a little latency
+ factory.setMakeLatency(200);
+ try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(factory, new GenericObjectPoolConfig<>())) {
+ final String s = pool.borrowObject();
+ // First borrow waits on create, so wait time should be at least 200 ms
+ // Allow 100ms error in clock times
+ assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100);
+ assertTrue(pool.getMeanBorrowWaitTimeMillis() >= 100);
+ pool.returnObject(s);
+ pool.borrowObject();
+ // Second borrow does not have to wait on create, average should be about 100
+ assertTrue(pool.getMaxBorrowWaitTimeMillis() > 100);
+ assertTrue(pool.getMeanBorrowWaitTimeMillis() < 200);
+ assertTrue(pool.getMeanBorrowWaitTimeMillis() > 20);
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testCloseMultiplePools1() {
+ try (final GenericObjectPool<String, TestException> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
+ genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
+ genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
+ }
+ genericObjectPool.close();
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testCloseMultiplePools2() throws Exception {
+ try (final GenericObjectPool<String, TestException> genericObjectPool2 = new GenericObjectPool<>(simpleFactory)) {
+ // Ensure eviction takes a long time, during which time EvictionTimer.executor's queue is empty
+ simpleFactory.setDestroyLatency(1000L);
+ // Ensure there is an object to evict, so that above latency takes effect
+ genericObjectPool.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
+ genericObjectPool2.setTimeBetweenEvictionRuns(TestConstants.ONE_MILLISECOND_DURATION);
+ genericObjectPool.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
+ genericObjectPool2.setMinEvictableIdleTime(TestConstants.ONE_MILLISECOND_DURATION);
+ genericObjectPool.addObject();
+ genericObjectPool2.addObject();
+ // Close both pools
+ }
+ genericObjectPool.close();
+ }
+
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testConcurrentBorrowAndEvict() throws Exception {
+
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.addObject();
+
+ for (int i = 0; i < 5000; i++) {
+ final ConcurrentBorrowAndEvictThread one =
+ new ConcurrentBorrowAndEvictThread(true);
+ final ConcurrentBorrowAndEvictThread two =
+ new ConcurrentBorrowAndEvictThread(false);
+
+ one.start();
+ two.start();
+ one.join();
+ two.join();
+
+ genericObjectPool.returnObject(one.obj);
+
+ /* Uncomment this for a progress indication
+ if (i % 10 == 0) {
+ System.out.println(i/10);
+ }
+ */
+ }
+ }
+
+ /**
+ * POOL-231 - verify that concurrent invalidates of the same object do not
+ * corrupt pool destroyCount.
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ public void testConcurrentInvalidate() throws Exception {
+ // Get allObjects and idleObjects loaded with some instances
+ final int nObjects = 1000;
+ genericObjectPool.setMaxTotal(nObjects);
+ genericObjectPool.setMaxIdle(nObjects);
+ final String[] obj = new String[nObjects];
+ for (int i = 0; i < nObjects; i++) {
+ obj[i] = genericObjectPool.borrowObject();
+ }
+ for (int i = 0; i < nObjects; i++) {
+ if (i % 2 == 0) {
+ genericObjectPool.returnObject(obj[i]);
+ }
+ }
+ final int nThreads = 20;
+ final int nIterations = 60;
+ final InvalidateThread[] threads = new InvalidateThread[nThreads];
+ // Randomly generated list of distinct invalidation targets
+ final ArrayList<Integer> targets = new ArrayList<>();
+ final Random random = new Random();
+ for (int j = 0; j < nIterations; j++) {
+ // Get a random invalidation target
+ Integer targ = Integer.valueOf(random.nextInt(nObjects));
+ while (targets.contains(targ)) {
+ targ = Integer.valueOf(random.nextInt(nObjects));
+ }
+ targets.add(targ);
+ // Launch nThreads threads all trying to invalidate the target
+ for (int i = 0; i < nThreads; i++) {
+ threads[i] = new InvalidateThread(genericObjectPool, obj[targ.intValue()]);
+ }
+ for (int i = 0; i < nThreads; i++) {
+ new Thread(threads[i]).start();
+ }
+ boolean done = false;
+ while (!done) {
+ done = true;
+ for (int i = 0; i < nThreads; i++) {
+ done = done && threads[i].complete();
+ }
+ Thread.sleep(100);
+ }
+ }
+ assertEquals(nIterations, genericObjectPool.getDestroyedCount());
+ }
+
+ @Test
+ public void testConstructorNullFactory() {
+ // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
+ assertThrows(IllegalArgumentException.class,
+ () -> new GenericObjectPool<>(null));
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testConstructors() {
+
+ // Make constructor arguments all different from defaults
+ final int minIdle = 2;
+ final Duration maxWaitDuration = Duration.ofMillis(3);
+ final long maxWaitMillis = maxWaitDuration.toMillis();
+ final int maxIdle = 4;
+ final int maxTotal = 5;
+ final Duration minEvictableIdleDuration = Duration.ofMillis(6);
+ final long minEvictableIdleMillis = minEvictableIdleDuration.toMillis();
+ final int numTestsPerEvictionRun = 7;
+ final boolean testOnBorrow = true;
+ final boolean testOnReturn = true;
+ final boolean testWhileIdle = true;
+ final long timeBetweenEvictionRunsMillis = 8;
+ final boolean blockWhenExhausted = false;
+ final boolean lifo = false;
+ final PooledObjectFactory<Object, RuntimeException> dummyFactory = new DummyFactory();
+ try (GenericObjectPool<Object, RuntimeException> dummyPool = new GenericObjectPool<>(dummyFactory)) {
+ assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_IDLE, dummyPool.getMaxIdle());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, dummyPool.getMaxWaitMillis());
+ assertEquals(GenericObjectPoolConfig.DEFAULT_MIN_IDLE, dummyPool.getMinIdle());
+ assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+ dummyPool.getMinEvictableIdleTimeMillis());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
+ dummyPool.getMinEvictableIdleTime());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
+ dummyPool.getMinEvictableIdleDuration());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
+ dummyPool.getNumTestsPerEvictionRun());
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW),
+ Boolean.valueOf(dummyPool.getTestOnBorrow()));
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN),
+ Boolean.valueOf(dummyPool.getTestOnReturn()));
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE),
+ Boolean.valueOf(dummyPool.getTestWhileIdle()));
+ assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
+ dummyPool.getDurationBetweenEvictionRuns());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+ dummyPool.getTimeBetweenEvictionRunsMillis());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
+ dummyPool.getTimeBetweenEvictionRuns());
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED),
+ Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo()));
+ }
+
+ final GenericObjectPoolConfig<Object> config = new GenericObjectPoolConfig<>();
+ config.setLifo(lifo);
+ config.setMaxIdle(maxIdle);
+ config.setMinIdle(minIdle);
+ config.setMaxTotal(maxTotal);
+ config.setMaxWait(maxWaitDuration);
+ config.setMinEvictableIdleTimeMillis(minEvictableIdleMillis);
+ assertEquals(minEvictableIdleMillis, config.getMinEvictableIdleTime().toMillis());
+ config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
+ config.setTestOnBorrow(testOnBorrow);
+ config.setTestOnReturn(testOnReturn);
+ config.setTestWhileIdle(testWhileIdle);
+ config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+ assertEquals(timeBetweenEvictionRunsMillis, config.getTimeBetweenEvictionRuns().toMillis());
+ config.setBlockWhenExhausted(blockWhenExhausted);
+ try (GenericObjectPool<Object, RuntimeException> dummyPool = new GenericObjectPool<>(dummyFactory, config)) {
+ assertEquals(maxIdle, dummyPool.getMaxIdle());
+ assertEquals(maxWaitDuration, dummyPool.getMaxWaitDuration());
+ assertEquals(maxWaitMillis, dummyPool.getMaxWaitMillis());
+ assertEquals(minIdle, dummyPool.getMinIdle());
+ assertEquals(maxTotal, dummyPool.getMaxTotal());
+ assertEquals(minEvictableIdleMillis, dummyPool.getMinEvictableIdleTimeMillis());
+ assertEquals(numTestsPerEvictionRun, dummyPool.getNumTestsPerEvictionRun());
+ assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(dummyPool.getTestOnBorrow()));
+ assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(dummyPool.getTestOnReturn()));
+ assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(dummyPool.getTestWhileIdle()));
+ assertEquals(timeBetweenEvictionRunsMillis, dummyPool.getTimeBetweenEvictionRunsMillis());
+ assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(dummyPool.getBlockWhenExhausted()));
+ assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(dummyPool.getLifo()));
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testDefaultConfiguration() {
+ assertConfiguration(new GenericObjectPoolConfig<>(),genericObjectPool);
+ }
+
+ /**
+ * Verifies that when a factory's makeObject produces instances that are not
+ * discernible by equals, the pool can handle them.
+ *
+ * JIRA: POOL-283
+ */
+ @Test
+ public void testEqualsIndiscernible() throws Exception {
+ final HashSetFactory factory = new HashSetFactory();
+ try (final GenericObjectPool<HashSet<String>, RuntimeException> pool = new GenericObjectPool<>(factory,
+ new GenericObjectPoolConfig<>())) {
+ final HashSet<String> s1 = pool.borrowObject();
+ final HashSet<String> s2 = pool.borrowObject();
+ pool.returnObject(s1);
+ pool.returnObject(s2);
+ }
+ }
+
+ @Test
+ public void testErrorFactoryDoesNotBlockThreads() throws Exception {
+
+ final CreateErrorFactory factory = new CreateErrorFactory();
+ try (final GenericObjectPool<String, InterruptedException> createFailFactoryPool = new GenericObjectPool<>(factory)) {
+
+ createFailFactoryPool.setMaxTotal(1);
+
+ // Try and borrow the first object from the pool
+ final WaitingTestThread<InterruptedException> thread1 = new WaitingTestThread<>(createFailFactoryPool, 0);
+ thread1.start();
+
+ // Wait for thread to reach semaphore
+ while (!factory.hasQueuedThreads()) {
+ Thread.sleep(200);
+ }
+
+ // Try and borrow the second object from the pool
+ final WaitingTestThread<InterruptedException> thread2 = new WaitingTestThread<>(createFailFactoryPool, 0);
+ thread2.start();
+ // Pool will not call factory since maximum number of object creations
+ // are already queued.
+
+ // Thread 2 will wait on an object being returned to the pool
+ // Give thread 2 a chance to reach this state
+ Thread.sleep(1000);
+
+ // Release thread1
+ factory.release();
+ // Pre-release thread2
+ factory.release();
+
+ // Both threads should now complete.
+ boolean threadRunning = true;
+ int count = 0;
+ while (threadRunning && count < 15) {
+ threadRunning = thread1.isAlive();
+ threadRunning = thread2.isAlive();
+ Thread.sleep(200);
+ count++;
+ }
+ assertFalse(thread1.isAlive());
+ assertFalse(thread2.isAlive());
+
+ assertTrue(thread1.thrown instanceof UnknownError);
+ assertTrue(thread2.thrown instanceof UnknownError);
+ }
+ }
+
+ /**
+ * Tests addObject contention between ensureMinIdle triggered by
+ * the Evictor with minIdle > 0 and borrowObject.
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictAddObjects() throws Exception {
+ simpleFactory.setMakeLatency(300);
+ simpleFactory.setMaxTotal(2);
+ genericObjectPool.setMaxTotal(2);
+ genericObjectPool.setMinIdle(1);
+ genericObjectPool.borrowObject(); // numActive = 1, numIdle = 0
+ // Create a test thread that will run once and try a borrow after
+ // 150ms fixed delay
+ final TestThread<String, TestException> borrower = new TestThread<>(genericObjectPool, 1, 150, false);
+ final Thread borrowerThread = new Thread(borrower);
+ // Set evictor to run in 100 ms - will create idle instance
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ borrowerThread.start(); // Off to the races
+ borrowerThread.join();
+ assertFalse(borrower.failed());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictFIFO() throws Exception {
+ checkEvict(false);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEviction() throws Exception {
+ genericObjectPool.setMaxIdle(500);
+ genericObjectPool.setMaxTotal(500);
+ genericObjectPool.setNumTestsPerEvictionRun(100);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+ genericObjectPool.setTestWhileIdle(true);
+
+ final String[] active = new String[500];
+ for (int i = 0; i < 500; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ for (int i = 0; i < 500; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ Waiter.sleepQuietly(1000L);
+ assertTrue(genericObjectPool.getNumIdle() < 500,"Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 400,"Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 300,"Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 200,"Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 100,"Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle());
+
+ for (int i = 0; i < 500; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ for (int i = 0; i < 500; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ Waiter.sleepQuietly(1000L);
+ assertTrue(genericObjectPool.getNumIdle() < 500,"Should be less than 500 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 400,"Should be less than 400 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 300,"Should be less than 300 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 200,"Should be less than 200 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertTrue(genericObjectPool.getNumIdle() < 100,"Should be less than 100 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(600L);
+ assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictionInvalid() throws Exception {
+
+ try (final GenericObjectPool<Object, RuntimeException> invalidFactoryPool = new GenericObjectPool<>(new InvalidFactory())) {
+
+ invalidFactoryPool.setMaxIdle(1);
+ invalidFactoryPool.setMaxTotal(1);
+ invalidFactoryPool.setTestOnBorrow(false);
+ invalidFactoryPool.setTestOnReturn(false);
+ invalidFactoryPool.setTestWhileIdle(true);
+ invalidFactoryPool.setMinEvictableIdleTime(Duration.ofSeconds(100));
+ invalidFactoryPool.setNumTestsPerEvictionRun(1);
+
+ final Object p = invalidFactoryPool.borrowObject();
+ invalidFactoryPool.returnObject(p);
+
+ // Run eviction in a separate thread
+ final Thread t = new EvictionThread<>(invalidFactoryPool);
+ t.start();
+
+ // Sleep to make sure evictor has started
+ Thread.sleep(300);
+
+ try {
+ invalidFactoryPool.borrowObject(1);
+ } catch (final NoSuchElementException nsee) {
+ // Ignore
+ }
+
+ // Make sure evictor has finished
+ Thread.sleep(1000);
+
+ // Should have an empty pool
+ assertEquals( 0, invalidFactoryPool.getNumIdle(),"Idle count different than expected.");
+ assertEquals( 0, invalidFactoryPool.getNumActive(),"Total count different than expected.");
+ }
+ }
+
+ /**
+ * Test to make sure evictor visits least recently used objects first,
+ * regardless of FIFO/LIFO.
+ *
+ * JIRA: POOL-86
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictionOrder() throws Exception {
+ checkEvictionOrder(false);
+ tearDown();
+ setUp();
+ checkEvictionOrder(true);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictionPolicy() throws Exception {
+ genericObjectPool.setMaxIdle(500);
+ genericObjectPool.setMaxTotal(500);
+ genericObjectPool.setNumTestsPerEvictionRun(500);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(250));
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+ genericObjectPool.setTestWhileIdle(true);
+
+ // ClassNotFoundException
+ assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(Long.toString(System.currentTimeMillis())),
+ "setEvictionPolicyClassName must throw an error if the class name is invalid.");
+
+ // InstantiationException
+ assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(java.io.Serializable.class.getName()),
+ "setEvictionPolicyClassName must throw an error if the class name is invalid.");
+
+ // IllegalAccessException
+ assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(java.util.Collections.class.getName()),
+ "setEvictionPolicyClassName must throw an error if the class name is invalid.");
+
+ assertThrows(IllegalArgumentException.class, () -> genericObjectPool.setEvictionPolicyClassName(java.lang.String.class.getName()),
+ () -> "setEvictionPolicyClassName must throw an error if a class that does not implement EvictionPolicy is specified.");
+
+ genericObjectPool.setEvictionPolicy(new TestEvictionPolicy<>());
+ assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
+
+ genericObjectPool.setEvictionPolicyClassName(TestEvictionPolicy.class.getName());
+ assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName());
+
+ final String[] active = new String[500];
+ for (int i = 0; i < 500; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ for (int i = 0; i < 500; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ // Eviction policy ignores first 1500 attempts to evict and then always
+ // evicts. After 1s, there should have been two runs of 500 tests so no
+ // evictions
+ Waiter.sleepQuietly(1000L);
+ assertEquals(500, genericObjectPool.getNumIdle(), "Should be 500 idle");
+ // A further 1s wasn't enough so allow 2s for the evictor to clear out
+ // all of the idle objects.
+ Waiter.sleepQuietly(2000L);
+ assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle");
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictionSoftMinIdle() throws Exception {
+ class TimeTest extends BasePooledObjectFactory<TimeTest, RuntimeException> {
+ private final long createTimeMillis;
+
+ public TimeTest() {
+ createTimeMillis = System.currentTimeMillis();
+ }
+
+ @Override
+ public TimeTest create() {
+ return new TimeTest();
+ }
+
+ public long getCreateTimeMillis() {
+ return createTimeMillis;
+ }
+
+ @Override
+ public PooledObject<TimeTest> wrap(final TimeTest value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
+ try (final GenericObjectPool<TimeTest, RuntimeException> timePool = new GenericObjectPool<>(new TimeTest())) {
+
+ timePool.setMaxIdle(5);
+ timePool.setMaxTotal(5);
+ timePool.setNumTestsPerEvictionRun(5);
+ timePool.setMinEvictableIdle(Duration.ofSeconds(3));
+ timePool.setMinEvictableIdleTime(Duration.ofSeconds(3));
+ timePool.setSoftMinEvictableIdleTime(TestConstants.ONE_SECOND_DURATION);
+ timePool.setMinIdle(2);
+
+ final TimeTest[] active = new TimeTest[5];
+ final Long[] creationTime = new Long[5];
+ for (int i = 0; i < 5; i++) {
+ active[i] = timePool.borrowObject();
+ creationTime[i] = Long.valueOf(active[i].getCreateTimeMillis());
+ }
+
+ for (int i = 0; i < 5; i++) {
+ timePool.returnObject(active[i]);
+ }
+
+ // Soft evict all but minIdle(2)
+ Thread.sleep(1500L);
+ timePool.evict();
+ assertEquals( 2, timePool.getNumIdle(),"Idle count different than expected.");
+
+ // Hard evict the rest.
+ Thread.sleep(2000L);
+ timePool.evict();
+ assertEquals( 0, timePool.getNumIdle(),"Idle count different than expected.");
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictionWithNegativeNumTests() throws Exception {
+ // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test
+ genericObjectPool.setMaxIdle(6);
+ genericObjectPool.setMaxTotal(6);
+ genericObjectPool.setNumTestsPerEvictionRun(-2);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+
+ final String[] active = new String[6];
+ for (int i = 0; i < 6; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ for (int i = 0; i < 6; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ Waiter.sleepQuietly(100L);
+ assertTrue(genericObjectPool.getNumIdle() <= 6,"Should at most 6 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(100L);
+ assertTrue(genericObjectPool.getNumIdle() <= 3,"Should at most 3 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(100L);
+ assertTrue(genericObjectPool.getNumIdle() <= 2,"Should be at most 2 idle, found " + genericObjectPool.getNumIdle());
+ Waiter.sleepQuietly(100L);
+ assertEquals(0,genericObjectPool.getNumIdle(),"Should be zero idle, found " + genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictLIFO() throws Exception {
+ checkEvict(true);
+ }
+
+ /**
+ * Verifies that the evictor visits objects in expected order
+ * and frequency.
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ public void testEvictorVisiting() throws Exception {
+ checkEvictorVisiting(true);
+ checkEvictorVisiting(false);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictWhileEmpty() throws Exception {
+ genericObjectPool.evict();
+ genericObjectPool.evict();
+ genericObjectPool.close();
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testExceptionInValidationDuringEviction() throws Exception {
+ genericObjectPool.setMaxIdle(1);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ZERO);
+ genericObjectPool.setTestWhileIdle(true);
+
+ final String active = genericObjectPool.borrowObject();
+ genericObjectPool.returnObject(active);
+
+ simpleFactory.setThrowExceptionOnValidate(true);
+
+ assertThrows(RuntimeException.class, () -> genericObjectPool.evict());
+ assertEquals(0, genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testExceptionOnActivateDuringBorrow() throws Exception {
+ final String obj1 = genericObjectPool.borrowObject();
+ final String obj2 = genericObjectPool.borrowObject();
+ genericObjectPool.returnObject(obj1);
+ genericObjectPool.returnObject(obj2);
+ simpleFactory.setThrowExceptionOnActivate(true);
+ simpleFactory.setEvenValid(false);
+ // Activation will now throw every other time
+ // First attempt throws, but loop continues and second succeeds
+ final String obj = genericObjectPool.borrowObject();
+ assertEquals(1, genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+
+ genericObjectPool.returnObject(obj);
+ simpleFactory.setValid(false);
+ // Validation will now fail on activation when borrowObject returns
+ // an idle instance, and then when attempting to create a new instance
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ assertEquals(0, genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testExceptionOnDestroyDuringBorrow() throws Exception {
+ simpleFactory.setThrowExceptionOnDestroy(true);
+ genericObjectPool.setTestOnBorrow(true);
+ genericObjectPool.borrowObject();
+ simpleFactory.setValid(false); // Make validation fail on next borrow attempt
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ assertEquals(1, genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testExceptionOnDestroyDuringReturn() throws Exception {
+ simpleFactory.setThrowExceptionOnDestroy(true);
+ genericObjectPool.setTestOnReturn(true);
+ final String obj1 = genericObjectPool.borrowObject();
+ genericObjectPool.borrowObject();
+ simpleFactory.setValid(false); // Make validation fail
+ genericObjectPool.returnObject(obj1);
+ assertEquals(1, genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testExceptionOnPassivateDuringReturn() throws Exception {
+ final String obj = genericObjectPool.borrowObject();
+ simpleFactory.setThrowExceptionOnPassivate(true);
+ genericObjectPool.returnObject(obj);
+ assertEquals(0,genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ public void testFailingFactoryDoesNotBlockThreads() throws Exception {
+
+ final CreateFailFactory factory = new CreateFailFactory();
+ try (final GenericObjectPool<String, InterruptedException> createFailFactoryPool = new GenericObjectPool<>(factory)) {
+
+ createFailFactoryPool.setMaxTotal(1);
+
+ // Try and borrow the first object from the pool
+ final WaitingTestThread<InterruptedException> thread1 = new WaitingTestThread<>(createFailFactoryPool, 0);
+ thread1.start();
+
+ // Wait for thread to reach semaphore
+ while (!factory.hasQueuedThreads()) {
+ Thread.sleep(200);
+ }
+
+ // Try and borrow the second object from the pool
+ final WaitingTestThread<InterruptedException> thread2 = new WaitingTestThread<>(createFailFactoryPool, 0);
+ thread2.start();
+ // Pool will not call factory since maximum number of object creations
+ // are already queued.
+
+ // Thread 2 will wait on an object being returned to the pool
+ // Give thread 2 a chance to reach this state
+ Thread.sleep(1000);
+
+ // Release thread1
+ factory.release();
+ // Pre-release thread2
+ factory.release();
+
+ // Both threads should now complete.
+ boolean threadRunning = true;
+ int count = 0;
+ while (threadRunning && count < 15) {
+ threadRunning = thread1.isAlive();
+ threadRunning = thread2.isAlive();
+ Thread.sleep(200);
+ count++;
+ }
+ assertFalse(thread1.isAlive());
+ assertFalse(thread2.isAlive());
+
+ assertTrue(thread1.thrown instanceof UnsupportedCharsetException);
+ assertTrue(thread2.thrown instanceof UnsupportedCharsetException);
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testFIFO() throws Exception {
+ genericObjectPool.setLifo(false);
+ genericObjectPool.addObject(); // "0"
+ genericObjectPool.addObject(); // "1"
+ genericObjectPool.addObject(); // "2"
+ assertEquals( "0", genericObjectPool.borrowObject(),"Oldest");
+ assertEquals( "1", genericObjectPool.borrowObject(),"Middle");
+ assertEquals( "2", genericObjectPool.borrowObject(),"Youngest");
+ final String o = genericObjectPool.borrowObject();
+ assertEquals( "3", o,"new-3");
+ genericObjectPool.returnObject(o);
+ assertEquals( o, genericObjectPool.borrowObject(),"returned-3");
+ assertEquals( "4", genericObjectPool.borrowObject(),"new-4");
+ }
+
+ @Test
+ public void testGetFactoryType_DefaultPooledObjectFactory() {
+ try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(createDefaultPooledObjectFactory())) {
+ assertNotNull(pool.getFactoryType());
+ }
+ }
+
+ @Test
+ public void testGetFactoryType_NullPooledObjectFactory() {
+ try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(createNullPooledObjectFactory())) {
+ assertNotNull(pool.getFactoryType());
+ }
+ }
+
+ @Test
+ public void testGetFactoryType_PoolUtilsSynchronizedDefaultPooledFactory() {
+ try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
+ PoolUtils.synchronizedPooledFactory(createDefaultPooledObjectFactory()))) {
+ assertNotNull(pool.getFactoryType());
+ }
+ }
+
+ @Test
+ public void testGetFactoryType_PoolUtilsSynchronizedNullPooledFactory() {
+ try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
+ PoolUtils.synchronizedPooledFactory(createNullPooledObjectFactory()))) {
+ assertNotNull(pool.getFactoryType());
+ }
+ }
+
+ @Test
+ public void testGetFactoryType_SynchronizedDefaultPooledObjectFactory() {
+ try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
+ new TestSynchronizedPooledObjectFactory<>(createDefaultPooledObjectFactory()))) {
+ assertNotNull(pool.getFactoryType());
+ }
+ }
+
+ @Test
+ public void testGetFactoryType_SynchronizedNullPooledObjectFactory() {
+ try (final GenericObjectPool<String, RuntimeException> pool = new GenericObjectPool<>(
+ new TestSynchronizedPooledObjectFactory<>(createNullPooledObjectFactory()))) {
+ assertNotNull(pool.getFactoryType());
+ }
+ }
+
+ @Test
+ public void testGetStatsString() {
+ try (final GenericObjectPool<String, RuntimeException> 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.
+ *
+ * JIRA: POOL-240
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ public void testInvalidateFreesCapacity() throws Exception {
+ final SimpleFactory factory = new SimpleFactory();
+ try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(factory)) {
+ pool.setMaxTotal(2);
+ pool.setMaxWaitMillis(500);
+ // Borrow an instance and hold if for 5 seconds
+ final WaitingTestThread<TestException> thread1 = new WaitingTestThread<>(pool, 5000);
+ thread1.start();
+ // Borrow another instance
+ final String obj = pool.borrowObject();
+ // Launch another thread - will block, but fail in 500 ms
+ final WaitingTestThread<TestException> thread2 = new WaitingTestThread<>(pool, 100);
+ thread2.start();
+ // Invalidate the object borrowed by this thread - should allow thread2 to create
+ Thread.sleep(20);
+ pool.invalidateObject(obj);
+ Thread.sleep(600); // Wait for thread2 to timeout
+ if (thread2.thrown != null) {
+ fail(thread2.thrown.toString());
+ }
+ }
+ }
+
+ /**
+ * Ensure the pool is registered.
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testJmxRegistration() {
+ final ObjectName oname = genericObjectPool.getJmxName();
+ final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ final Set<ObjectName> result = mbs.queryNames(oname, null);
+ assertEquals(1, result.size());
+ genericObjectPool.jmxUnregister();
+
+ final GenericObjectPoolConfig<String> config = new GenericObjectPoolConfig<>();
+ config.setJmxEnabled(false);
+ try (final GenericObjectPool<String, TestException> poolWithoutJmx = new GenericObjectPool<>(simpleFactory, config)) {
+ assertNull(poolWithoutJmx.getJmxName());
+ config.setJmxEnabled(true);
+ poolWithoutJmx.jmxUnregister();
+ }
+
+ config.setJmxNameBase(null);
+ try (final GenericObjectPool<String, TestException> poolWithDefaultJmxNameBase = new GenericObjectPool<>(simpleFactory, config)) {
+ assertNotNull(poolWithDefaultJmxNameBase.getJmxName());
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testLIFO() throws Exception {
+ final String o;
+ genericObjectPool.setLifo(true);
+ genericObjectPool.addObject(); // "0"
+ genericObjectPool.addObject(); // "1"
+ genericObjectPool.addObject(); // "2"
+ assertEquals( "2", genericObjectPool.borrowObject(),"Youngest");
+ assertEquals( "1", genericObjectPool.borrowObject(),"Middle");
+ assertEquals( "0", genericObjectPool.borrowObject(),"Oldest");
+ o = genericObjectPool.borrowObject();
+ assertEquals( "3", o,"new-3");
+ genericObjectPool.returnObject(o);
+ assertEquals( o, genericObjectPool.borrowObject(),"returned-3");
+ assertEquals( "4", genericObjectPool.borrowObject(),"new-4");
+ }
+
+ /**
+ * Test the following scenario:
+ * Thread 1 borrows an instance
+ * Thread 2 starts to borrow another instance before thread 1 returns its instance
+ * Thread 1 returns its instance while thread 2 is validating its newly created instance
+ * The test verifies that the instance created by Thread 2 is not leaked.
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMakeConcurrentWithReturn() throws Exception {
+ genericObjectPool.setTestOnBorrow(true);
+ simpleFactory.setValid(true);
+ // Borrow and return an instance, with a short wait
+ final WaitingTestThread<TestException> thread1 = new WaitingTestThread<>(genericObjectPool, 200);
+ thread1.start();
+ Thread.sleep(50); // wait for validation to succeed
+ // Slow down validation and borrow an instance
+ simpleFactory.setValidateLatency(400);
+ final String instance = genericObjectPool.borrowObject();
+ // Now make sure that we have not leaked an instance
+ assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle() + 1);
+ genericObjectPool.returnObject(instance);
+ assertEquals(simpleFactory.getMakeCounter(), genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxIdle() throws Exception {
+ genericObjectPool.setMaxTotal(100);
+ genericObjectPool.setMaxIdle(8);
+ final String[] active = new String[100];
+ for(int i=0;i<100;i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ assertEquals(100,genericObjectPool.getNumActive());
+ assertEquals(0,genericObjectPool.getNumIdle());
+ for(int i=0;i<100;i++) {
+ genericObjectPool.returnObject(active[i]);
+ assertEquals(99 - i,genericObjectPool.getNumActive());
+ assertEquals(i < 8 ? i+1 : 8,genericObjectPool.getNumIdle());
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxIdleZero() throws Exception {
+ genericObjectPool.setMaxTotal(100);
+ genericObjectPool.setMaxIdle(0);
+ final String[] active = new String[100];
+ for(int i=0;i<100;i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ assertEquals(100,genericObjectPool.getNumActive());
+ assertEquals(0,genericObjectPool.getNumIdle());
+ for(int i=0;i<100;i++) {
+ genericObjectPool.returnObject(active[i]);
+ assertEquals(99 - i,genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+ }
+
+ /**
+ * Showcasing a possible deadlock situation as reported in POOL-356
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ @SuppressWarnings("rawtypes")
+ public void testMaxIdleZeroUnderLoad() {
+ // Config
+ final int numThreads = 199; // And main thread makes a round 200.
+ final int numIter = 20;
+ final int delay = 25;
+ final int maxTotal = 10;
+
+ simpleFactory.setMaxTotal(maxTotal);
+ genericObjectPool.setMaxTotal(maxTotal);
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
+
+ // this is important to trigger POOL-356
+ genericObjectPool.setMaxIdle(0);
+
+ // Start threads to borrow objects
+ final TestThread[] threads = new TestThread[numThreads];
+ for(int i=0;i<numThreads;i++) {
+ // Factor of 2 on iterations so main thread does work whilst other
+ // threads are running. Factor of 2 on delay so average delay for
+ // other threads == actual delay for main thread
+ threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
+ final Thread t = new Thread(threads[i]);
+ t.start();
+ }
+ // Give the threads a chance to start doing some work
+ Waiter.sleepQuietly(100L);
+
+ for (int i = 0; i < numIter; i++) {
+ String obj = null;
+ try {
+ Waiter.sleepQuietly(delay);
+ obj = genericObjectPool.borrowObject();
+ // Under load, observed numActive > maxTotal
+ if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
+ throw new IllegalStateException("Too many active objects");
+ }
+ Waiter.sleepQuietly(delay);
+ } catch (final Exception e) {
+ // Shouldn't happen
+ e.printStackTrace();
+ fail("Exception on borrow");
+ } finally {
+ if (obj != null) {
+ try {
+ genericObjectPool.returnObject(obj);
+ } catch (final Exception e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < numThreads; i++) {
+ while (!threads[i].complete()) {
+ Waiter.sleepQuietly(500L);
+ }
+ if (threads[i].failed()) {
+ threads[i].error.printStackTrace();
+ fail("Thread " + i + " failed: " + threads[i].error.toString());
+ }
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotal() throws Exception {
+ genericObjectPool.setMaxTotal(3);
+ genericObjectPool.setBlockWhenExhausted(false);
+
+ genericObjectPool.borrowObject();
+ genericObjectPool.borrowObject();
+ genericObjectPool.borrowObject();
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ }
+
+ /**
+ * Verifies that maxTotal is not exceeded when factory destroyObject
+ * has high latency, testOnReturn is set and there is high incidence of
+ * validation failures.
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalInvariant() {
+ final int maxTotal = 15;
+ simpleFactory.setEvenValid(false); // Every other validation fails
+ simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms
+ simpleFactory.setMaxTotal(maxTotal); // (makes - destroys) bound
+ simpleFactory.setValidationEnabled(true);
+ genericObjectPool.setMaxTotal(maxTotal);
+ genericObjectPool.setMaxIdle(-1);
+ genericObjectPool.setTestOnReturn(true);
+ genericObjectPool.setMaxWaitMillis(1000L);
+ runTestThreads(5, 10, 50, genericObjectPool);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ @SuppressWarnings("rawtypes")
+ public void testMaxTotalUnderLoad() {
+ // Config
+ final int numThreads = 199; // And main thread makes a round 200.
+ final int numIter = 20;
+ final int delay = 25;
+ final int maxTotal = 10;
+
+ simpleFactory.setMaxTotal(maxTotal);
+ genericObjectPool.setMaxTotal(maxTotal);
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
+
+ // Start threads to borrow objects
+ final TestThread[] threads = new TestThread[numThreads];
+ for(int i=0;i<numThreads;i++) {
+ // Factor of 2 on iterations so main thread does work whilst other
+ // threads are running. Factor of 2 on delay so average delay for
+ // other threads == actual delay for main thread
+ threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2);
+ final Thread t = new Thread(threads[i]);
+ t.start();
+ }
+ // Give the threads a chance to start doing some work
+ Waiter.sleepQuietly(5000);
+
+ for (int i = 0; i < numIter; i++) {
+ String obj = null;
+ try {
+ Waiter.sleepQuietly(delay);
+ obj = genericObjectPool.borrowObject();
+ // Under load, observed numActive > maxTotal
+ if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) {
+ throw new IllegalStateException("Too many active objects");
+ }
+ Waiter.sleepQuietly(delay);
+ } catch (final Exception e) {
+ // Shouldn't happen
+ e.printStackTrace();
+ fail("Exception on borrow");
+ } finally {
+ if (obj != null) {
+ try {
+ genericObjectPool.returnObject(obj);
+ } catch (final Exception e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < numThreads; i++) {
+ while(!threads[i].complete()) {
+ Waiter.sleepQuietly(500L);
+ }
+ if(threads[i].failed()) {
+ fail("Thread " + i + " failed: " + threads[i].error.toString());
+ }
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalZero() throws Exception {
+ genericObjectPool.setMaxTotal(0);
+ genericObjectPool.setBlockWhenExhausted(false);
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ }
+
+ /*
+ * Test multi-threaded pool access.
+ * Multiple threads, but maxTotal only allows half the threads to succeed.
+ *
+ * This test was prompted by Continuum build failures in the Commons DBCP test case:
+ * TestPerUserPoolDataSource.testMultipleThreads2()
+ * Let's see if the this fails on Continuum too!
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxWaitMultiThreaded() throws Exception {
+ final long maxWait = 500; // wait for connection
+ final long holdTime = 2 * maxWait; // how long to hold connection
+ final int threads = 10; // number of threads to grab the object initially
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setMaxWaitMillis(maxWait);
+ genericObjectPool.setMaxTotal(threads);
+ // Create enough threads so half the threads will have to wait
+ final WaitingTestThread<TestException>[] wtt = new WaitingTestThread[threads * 2];
+ for (int i = 0; i < wtt.length; i++) {
+ wtt[i] = new WaitingTestThread<>(genericObjectPool, holdTime);
+ }
+ final long originMillis = System.currentTimeMillis() - 1000;
+ for (final WaitingTestThread<TestException> element : wtt) {
+ element.start();
+ }
+ int failed = 0;
+ for (final WaitingTestThread<TestException> element : wtt) {
+ element.join();
+ if (element.thrown != null){
+ failed++;
+ }
+ }
+ if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){
+ System.out.println(
+ "MaxWait: " + maxWait +
+ " HoldTime: " + holdTime +
+ " MaxTotal: " + threads +
+ " Threads: " + wtt.length +
+ " Failed: " + failed
+ );
+ for (final WaitingTestThread<TestException> wt : wtt) {
+ System.out.println(
+ "PreBorrow: " + (wt.preBorrowMillis - originMillis) +
+ " PostBorrow: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - originMillis : -1) +
+ " BorrowTime: " + (wt.postBorrowMillis != 0 ? wt.postBorrowMillis - wt.preBorrowMillis : -1) +
+ " PostReturn: " + (wt.postReturnMillis != 0 ? wt.postReturnMillis - originMillis : -1) +
+ " Ended: " + (wt.endedMillis - originMillis) +
+ " ObjId: " + wt.objectId
+ );
+ }
+ }
+ assertEquals(wtt.length / 2, failed,"Expected half the threads to fail");
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMinIdle() throws Exception {
+ genericObjectPool.setMaxIdle(500);
+ genericObjectPool.setMinIdle(5);
+ genericObjectPool.setMaxTotal(10);
+ genericObjectPool.setNumTestsPerEvictionRun(0);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ genericObjectPool.setTestWhileIdle(true);
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
+
+ final String[] active = new String[5];
+ active[0] = genericObjectPool.borrowObject();
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
+
+ for (int i = 1; i < 5; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
+
+ for (int i = 0; i < 5; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMinIdleMaxTotal() throws Exception {
+ genericObjectPool.setMaxIdle(500);
+ genericObjectPool.setMinIdle(5);
+ genericObjectPool.setMaxTotal(10);
+ genericObjectPool.setNumTestsPerEvictionRun(0);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ genericObjectPool.setTestWhileIdle(true);
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
+
+ final String[] active = new String[10];
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
+
+ for (int i = 0; i < 5; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(5, genericObjectPool.getNumIdle(), "Should be 5 idle, found " + genericObjectPool.getNumIdle());
+
+ for(int i = 0 ; i < 5 ; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
+
+ for (int i = 0; i < 10; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(0, genericObjectPool.getNumIdle(), "Should be 0 idle, found " + genericObjectPool.getNumIdle());
+
+ for (int i = 0; i < 10; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+
+ Waiter.sleepQuietly(150L);
+ assertEquals(10, genericObjectPool.getNumIdle(), "Should be 10 idle, found " + genericObjectPool.getNumIdle());
+ }
+
+ /**
+ * Verifies that returning an object twice (without borrow in between) causes ISE
+ * but does not re-validate or re-passivate the instance.
+ *
+ * JIRA: POOL-285
+ */
+ @Test
+ public void testMultipleReturn() throws Exception {
+ final WaiterFactory<String> factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0);
+ try (final GenericObjectPool<Waiter, IllegalStateException> pool = new GenericObjectPool<>(factory)) {
+ pool.setTestOnReturn(true);
+ final Waiter waiter = pool.borrowObject();
+ pool.returnObject(waiter);
+ assertEquals(1, waiter.getValidationCount());
+ assertEquals(1, waiter.getPassivationCount());
+ try {
+ pool.returnObject(waiter);
+ fail("Expecting IllegalStateException from multiple return");
+ } catch (final IllegalStateException ex) {
+ // Exception is expected, now check no repeat validation/passivation
+ assertEquals(1, waiter.getValidationCount());
+ assertEquals(1, waiter.getPassivationCount());
+ }
+ }
+ }
+
+ // POOL-248
+ @Test
+ public void testMultipleReturnOfSameObject() throws Exception {
+ try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(simpleFactory, new GenericObjectPoolConfig<>())) {
+
+ assertEquals(0, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+
+ final String obj = pool.borrowObject();
+
+ assertEquals(1, pool.getNumActive());
+ assertEquals(0, pool.getNumIdle());
+
+ pool.returnObject(obj);
+
+ assertEquals(0, pool.getNumActive());
+ assertEquals(1, pool.getNumIdle());
+
+ assertThrows(IllegalStateException.class,
+ () -> pool.returnObject(obj));
+
+ assertEquals(0, pool.getNumActive());
+ assertEquals(1, pool.getNumIdle());
+ }
+ }
+
+ /**
+ * Verifies that when a borrowed object is mutated in a way that does not
+ * preserve equality and hashcode, the pool can recognized it on return.
+ *
+ * JIRA: POOL-284
+ */
+ @Test
+ public void testMutable() throws Exception {
+ final HashSetFactory factory = new HashSetFactory();
+ try (final GenericObjectPool<HashSet<String>, RuntimeException> pool = new GenericObjectPool<>(factory,
+ new GenericObjectPoolConfig<>())) {
+ final HashSet<String> s1 = pool.borrowObject();
+ final HashSet<String> s2 = pool.borrowObject();
+ s1.add("One");
+ s2.add("One");
+ pool.returnObject(s1);
+ pool.returnObject(s2);
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testNegativeMaxTotal() throws Exception {
+ genericObjectPool.setMaxTotal(-1);
+ genericObjectPool.setBlockWhenExhausted(false);
+ final String obj = genericObjectPool.borrowObject();
+ assertEquals(getNthObject(0),obj);
+ genericObjectPool.returnObject(obj);
+ }
+
+ /**
+ * Verifies that concurrent threads never "share" instances
+ */
+ @Test
+ public void testNoInstanceOverlap() {
+ final int maxTotal = 5;
+ final int numThreads = 100;
+ final int delay = 1;
+ final int iterations = 1000;
+ final AtomicIntegerFactory factory = new AtomicIntegerFactory();
+ try (final GenericObjectPool<AtomicInteger, RuntimeException> pool = new GenericObjectPool<>(factory)) {
+ pool.setMaxTotal(maxTotal);
+ pool.setMaxIdle(maxTotal);
+ pool.setTestOnBorrow(true);
+ pool.setBlockWhenExhausted(true);
+ pool.setMaxWaitMillis(-1);
+ runTestThreads(numThreads, iterations, delay, pool);
+ assertEquals(0, pool.getDestroyedByBorrowValidationCount());
+ }
+ }
+
+ /**
+ * POOL-376
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testNoInvalidateNPE() throws Exception {
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setTestOnCreate(true);
+ genericObjectPool.setMaxWaitMillis(-1);
+ final String obj = genericObjectPool.borrowObject();
+ // Make validation fail - this will cause create() to return null
+ simpleFactory.setValid(false);
+ // Create a take waiter
+ final WaitingTestThread<TestException> wtt = new WaitingTestThread<>(genericObjectPool, 200);
+ wtt.start();
+ // Give wtt time to start
+ Thread.sleep(200);
+ genericObjectPool.invalidateObject(obj);
+ // Now allow create to succeed so waiter can be served
+ simpleFactory.setValid(true);
+ }
+
+ public void testPreparePool() throws Exception {
+ genericObjectPool.setMinIdle(1);
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.preparePool();
+ assertEquals(1, genericObjectPool.getNumIdle());
+ final String obj = genericObjectPool.borrowObject();
+ genericObjectPool.preparePool();
+ assertEquals(0, genericObjectPool.getNumIdle());
+ genericObjectPool.setMinIdle(0);
+ genericObjectPool.returnObject(obj);
+ genericObjectPool.preparePool();
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+
+ @Test/* maxWaitMillis x2 + padding */
+ @Timeout(value = 1200, unit = TimeUnit.MILLISECONDS)
+ public void testReturnBorrowObjectWithingMaxWaitMillis() throws Exception {
+ final long maxWaitMillis = 500;
+
+ try (final GenericObjectPool<String, InterruptedException> createSlowObjectFactoryPool = new GenericObjectPool<>(
+ createSlowObjectFactory(60000))) {
+ createSlowObjectFactoryPool.setMaxTotal(1);
+ createSlowObjectFactoryPool.setMaxWaitMillis(maxWaitMillis);
+
+ // thread1 tries creating a slow object to make pool full.
+ final WaitingTestThread<InterruptedException> thread1 = new WaitingTestThread<>(createSlowObjectFactoryPool, 0);
+ thread1.start();
+
+ // Wait for thread1's reaching to create().
+ Thread.sleep(100);
+
+ // another one tries borrowObject. It should return within maxWaitMillis.
+ assertThrows(NoSuchElementException.class, () -> createSlowObjectFactoryPool.borrowObject(maxWaitMillis),
+ "borrowObject must fail due to timeout by maxWaitMillis");
+
+ assertTrue(thread1.isAlive());
+ }
+ }
+
+ /**
+ * This is the test case for POOL-263. It is disabled since it will always
+ * pass without artificial delay being injected into GOP.returnObject() and
+ * a way to this hasn't currently been found that doesn't involve
+ * polluting the GOP implementation. The artificial delay needs to be
+ * inserted just before the final call to isLifo() in the returnObject()
+ * method.
+ */
+ //@Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testReturnObject() throws Exception {
+
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setMaxIdle(-1);
+ final String active = genericObjectPool.borrowObject();
+
+ assertEquals(1, genericObjectPool.getNumActive());
+ assertEquals(0, genericObjectPool.getNumIdle());
+
+ final Thread t = new Thread(() -> genericObjectPool.close());
+ t.start();
+
+ genericObjectPool.returnObject(active);
+
+ // Wait for the close() thread to complete
+ while (t.isAlive()) {
+ Thread.sleep(50);
+ }
+
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testSetConfig() throws Exception {
+ final GenericObjectPoolConfig<String> expected = new GenericObjectPoolConfig<>();
+ assertConfiguration(expected,genericObjectPool);
+ expected.setMaxTotal(2);
+ expected.setMaxIdle(3);
+ expected.setMaxWait(Duration.ofMillis(5));
+ expected.setMinEvictableIdleTime(Duration.ofMillis(7L));
+ expected.setNumTestsPerEvictionRun(9);
+ expected.setTestOnCreate(true);
+ expected.setTestOnBorrow(true);
+ expected.setTestOnReturn(true);
+ expected.setTestWhileIdle(true);
+ expected.setTimeBetweenEvictionRuns(Duration.ofMillis(11L));
+ expected.setBlockWhenExhausted(false);
+ genericObjectPool.setConfig(expected);
+ assertConfiguration(expected,genericObjectPool);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testSettersAndGetters() throws Exception {
+ {
+ // The object receives an Exception during its creation to prevent
+ // memory leaks. See BaseGenericObjectPool constructor for more details.
+ assertNotEquals("", genericObjectPool.getCreationStackTrace());
+ }
+ {
+ assertEquals(0, genericObjectPool.getBorrowedCount());
+ }
+ {
+ assertEquals(0, genericObjectPool.getReturnedCount());
+ }
+ {
+ assertEquals(0, genericObjectPool.getCreatedCount());
+ }
+ {
+ assertEquals(0, genericObjectPool.getDestroyedCount());
+ }
+ {
+ assertEquals(0, genericObjectPool.getDestroyedByEvictorCount());
+ }
+ {
+ assertEquals(0, genericObjectPool.getDestroyedByBorrowValidationCount());
+ }
+ {
+ assertEquals(0, genericObjectPool.getMeanActiveTimeMillis());
+ }
+ {
+ assertEquals(0, genericObjectPool.getMeanIdleTimeMillis());
+ }
+ {
+ assertEquals(0, genericObjectPool.getMeanBorrowWaitTimeMillis());
+ }
+ {
+ assertEquals(0, genericObjectPool.getMaxBorrowWaitTimeMillis());
+ }
+ {
+ assertEquals(0, genericObjectPool.getNumIdle());
+ }
+ {
+ genericObjectPool.setMaxTotal(123);
+ assertEquals(123,genericObjectPool.getMaxTotal());
+ }
+ {
+ genericObjectPool.setMaxIdle(12);
+ assertEquals(12,genericObjectPool.getMaxIdle());
+ }
+ {
+ genericObjectPool.setMaxWaitMillis(1234L);
+ assertEquals(1234L,genericObjectPool.getMaxWaitMillis());
+ }
+ {
+ genericObjectPool.setMinEvictableIdleTimeMillis(12345L);
+ assertEquals(12345L,genericObjectPool.getMinEvictableIdleDuration().toMillis());
+ assertEquals(12345L,genericObjectPool.getMinEvictableIdleTimeMillis());
+ assertEquals(12345L,genericObjectPool.getMinEvictableIdleTime().toMillis());
+ }
+ {
+ genericObjectPool.setNumTestsPerEvictionRun(11);
+ assertEquals(11,genericObjectPool.getNumTestsPerEvictionRun());
+ }
+ {
+ genericObjectPool.setTestOnBorrow(true);
+ assertTrue(genericObjectPool.getTestOnBorrow());
+ genericObjectPool.setTestOnBorrow(false);
+ assertFalse(genericObjectPool.getTestOnBorrow());
+ }
+ {
+ genericObjectPool.setTestOnReturn(true);
+ assertTrue(genericObjectPool.getTestOnReturn());
+ genericObjectPool.setTestOnReturn(false);
+ assertFalse(genericObjectPool.getTestOnReturn());
+ }
+ {
+ genericObjectPool.setTestWhileIdle(true);
+ assertTrue(genericObjectPool.getTestWhileIdle());
+ genericObjectPool.setTestWhileIdle(false);
+ assertFalse(genericObjectPool.getTestWhileIdle());
+ }
+ {
+ genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L);
+ assertEquals(11235L,genericObjectPool.getDurationBetweenEvictionRuns().toMillis());
+ assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRunsMillis());
+ assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRuns().toMillis());
+ }
+ {
+ genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L);
+ assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleDuration().toMillis());
+ assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTimeMillis());
+ assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTime().toMillis());
+ }
+ {
+ genericObjectPool.setBlockWhenExhausted(true);
+ assertTrue(genericObjectPool.getBlockWhenExhausted());
+ genericObjectPool.setBlockWhenExhausted(false);
+ assertFalse(genericObjectPool.getBlockWhenExhausted());
+ }
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testStartAndStopEvictor() throws Exception {
+ // set up pool without evictor
+ genericObjectPool.setMaxIdle(6);
+ genericObjectPool.setMaxTotal(6);
+ genericObjectPool.setNumTestsPerEvictionRun(6);
+ genericObjectPool.setMinEvictableIdleTime(Duration.ofMillis(100));
+
+ for (int j = 0; j < 2; j++) {
+ // populate the pool
+ {
+ final String[] active = new String[6];
+ for (int i = 0; i < 6; i++) {
+ active[i] = genericObjectPool.borrowObject();
+ }
+ for (int i = 0; i < 6; i++) {
+ genericObjectPool.returnObject(active[i]);
+ }
+ }
+
+ // note that it stays populated
+ assertEquals(6,genericObjectPool.getNumIdle(),"Should have 6 idle");
+
+ // start the evictor
+ genericObjectPool.setTimeBetweenEvictionRuns(Duration.ofMillis(50));
+
+ // wait a second (well, .2 seconds)
+ Waiter.sleepQuietly(200L);
+
+ // assert that the evictor has cleared out the pool
+ assertEquals(0,genericObjectPool.getNumIdle(),"Should have 0 idle");
+
+ // stop the evictor
+ genericObjectPool.startEvictor(Duration.ZERO);
+ }
+ }
+
+ @Test
+ public void testSwallowedExceptionListener() {
+ genericObjectPool.setSwallowedExceptionListener(null); // must simply return
+ final List<Exception> swallowedExceptions = new ArrayList<>();
+ /*
+ * A simple listener, that will throw a OOM on 3rd exception.
+ */
+ final SwallowedExceptionListener listener = e -> {
+ if (swallowedExceptions.size() == 2) {
+ throw new OutOfMemoryError();
+ }
+ swallowedExceptions.add(e);
+ };
+ genericObjectPool.setSwallowedExceptionListener(listener);
+
+ final Exception e1 = new Exception();
+ final Exception e2 = new ArrayIndexOutOfBoundsException();
+
+ genericObjectPool.swallowException(e1);
+ genericObjectPool.swallowException(e2);
+
+ assertThrows(OutOfMemoryError.class, () -> genericObjectPool.swallowException(e1));
+
+ assertEquals(2, swallowedExceptions.size());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testThreaded1() throws Exception {
+ genericObjectPool.setMaxTotal(15);
+ genericObjectPool.setMaxIdle(15);
+ genericObjectPool.setMaxWaitMillis(1000L);
+ runTestThreads(20, 100, 50, genericObjectPool);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testTimeoutNoLeak() throws Exception {
+ genericObjectPool.setMaxTotal(2);
+ genericObjectPool.setMaxWaitMillis(10);
+ genericObjectPool.setBlockWhenExhausted(true);
+ final String obj = genericObjectPool.borrowObject();
+ final String obj2 = genericObjectPool.borrowObject();
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ genericObjectPool.returnObject(obj2);
+ genericObjectPool.returnObject(obj);
+
+ genericObjectPool.borrowObject();
+ genericObjectPool.borrowObject();
+ }
+
+ /**
+ * Tests POOL-361
+ */
+ @Test
+ public void testValidateOnCreate() throws Exception {
+ genericObjectPool.setTestOnCreate(true);
+ genericObjectPool.addObject();
+ assertEquals(1, simpleFactory.validateCounter);
+ }
+
+ /**
+ * Tests POOL-361
+ */
+ @Test
+ public void testValidateOnCreateFailure() throws Exception {
+ genericObjectPool.setTestOnCreate(true);
+ genericObjectPool.setTestOnBorrow(false);
+ genericObjectPool.setMaxTotal(2);
+ simpleFactory.setValid(false);
+ // Make sure failed validations do not leak capacity
+ genericObjectPool.addObject();
+ genericObjectPool.addObject();
+ assertEquals(0, genericObjectPool.getNumIdle());
+ assertEquals(0, genericObjectPool.getNumActive());
+ simpleFactory.setValid(true);
+ final String obj = genericObjectPool.borrowObject();
+ assertNotNull(obj);
+ genericObjectPool.addObject();
+ // Should have one idle, one out now
+ assertEquals(1, genericObjectPool.getNumIdle());
+ assertEquals(1, genericObjectPool.getNumActive());
+ }
+
+ /**
+ * Verify that threads waiting on a depleted pool get served when a returning object fails
+ * validation.
+ *
+ * JIRA: POOL-240
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ public void testValidationFailureOnReturnFreesCapacity() throws Exception {
+ final SimpleFactory factory = new SimpleFactory();
+ factory.setValid(false); // Validate will always fail
+ factory.setValidationEnabled(true);
+ try (final GenericObjectPool<String, TestException> pool = new GenericObjectPool<>(factory)) {
+ pool.setMaxTotal(2);
+ pool.setMaxWaitMillis(1500);
+ pool.setTestOnReturn(true);
+ pool.setTestOnBorrow(false);
+ // Borrow an instance and hold if for 5 seconds
+ final WaitingTestThread<TestException> thread1 = new WaitingTestThread<>(pool, 5000);
+ thread1.start();
+ // Borrow another instance and return it after 500 ms (validation will fail)
+ final WaitingTestThread<TestException> thread2 = new WaitingTestThread<>(pool, 500);
+ thread2.start();
+ Thread.sleep(50);
+ // Try to borrow an object
+ final String obj = pool.borrowObject();
+ pool.returnObject(obj);
+ }
+ }
+
+ // POOL-276
+ @Test
+ public void testValidationOnCreateOnly() throws Exception {
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setTestOnCreate(true);
+ genericObjectPool.setTestOnBorrow(false);
+ genericObjectPool.setTestOnReturn(false);
+ genericObjectPool.setTestWhileIdle(false);
+
+ final String o1 = genericObjectPool.borrowObject();
+ assertEquals("0", o1);
+ final Timer t = new Timer();
+ t.schedule(
+ new TimerTask() {
+ @Override
+ public void run() {
+ genericObjectPool.returnObject(o1);
+ }
+ }, 3000);
+
+ final String o2 = genericObjectPool.borrowObject();
+ assertEquals("0", o2);
+
+ assertEquals(1, simpleFactory.validateCounter);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testWhenExhaustedBlock() throws Exception {
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setMaxWaitMillis(10L);
+ final String obj1 = genericObjectPool.borrowObject();
+ assertNotNull(obj1);
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ genericObjectPool.returnObject(obj1);
+ genericObjectPool.close();
+ }
+
+ /**
+ * POOL-189
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testWhenExhaustedBlockClosePool() throws Exception {
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setMaxWaitMillis(-1);
+ final Object obj1 = genericObjectPool.borrowObject();
+
+ // Make sure an object was obtained
+ assertNotNull(obj1);
+
+ // Create a separate thread to try and borrow another object
+ final WaitingTestThread<TestException> wtt = new WaitingTestThread<>(genericObjectPool, 200);
+ wtt.start();
+ // Give wtt time to start
+ Thread.sleep(200);
+
+ // close the pool (Bug POOL-189)
+ genericObjectPool.close();
+
+ // Give interrupt time to take effect
+ Thread.sleep(200);
+
+ // Check thread was interrupted
+ assertTrue(wtt.thrown instanceof InterruptedException);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testWhenExhaustedBlockInterrupt() throws Exception {
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setBlockWhenExhausted(true);
+ genericObjectPool.setMaxWaitMillis(-1);
+ final String obj1 = genericObjectPool.borrowObject();
+
+ // Make sure on object was obtained
+ assertNotNull(obj1);
+
+ // Create a separate thread to try and borrow another object
+ final WaitingTestThread<TestException> wtt = new WaitingTestThread<>(genericObjectPool, 200000);
+ wtt.start();
+ // Give wtt time to start
+ Thread.sleep(200);
+ wtt.interrupt();
+
+ // Give interrupt time to take effect
+ Thread.sleep(200);
+
+ // Check thread was interrupted
+ assertTrue(wtt.thrown instanceof InterruptedException);
+
+ // Return object to the pool
+ genericObjectPool.returnObject(obj1);
+
+ // Bug POOL-162 - check there is now an object in the pool
+ genericObjectPool.setMaxWaitMillis(10L);
+ String obj2 = null;
+ try {
+ obj2 = genericObjectPool.borrowObject();
+ assertNotNull(obj2);
+ } catch (final NoSuchElementException e) {
+ // Not expected
+ fail("NoSuchElementException not expected");
+ }
+ genericObjectPool.returnObject(obj2);
+ genericObjectPool.close();
+
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testWhenExhaustedFail() throws Exception {
+ genericObjectPool.setMaxTotal(1);
+ genericObjectPool.setBlockWhenExhausted(false);
+ final String obj1 = genericObjectPool.borrowObject();
+ assertNotNull(obj1);
+ assertThrows(NoSuchElementException.class, () -> genericObjectPool.borrowObject());
+ genericObjectPool.returnObject(obj1);
+ assertEquals(1, genericObjectPool.getNumIdle());
+ genericObjectPool.close();
+ }
+
+}