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 2021/02/15 23:56:03 UTC
[commons-pool] branch master updated (9df8d27 -> a6930db)
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git.
from 9df8d27 Javadoc: Use "Gets..." for getters and "Borrows..." for borrowers.
new a604ec2 Sort members.
new f238079 Sort members.
new 847d70d Javadoc formatting fixes.
new a6930db Add and use java.time.Duration APIs timeouts instead of using ints for seconds.
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/commons/pool2/PooledObject.java | 37 +-
.../commons/pool2/impl/BaseGenericObjectPool.java | 25 +-
.../commons/pool2/impl/GenericKeyedObjectPool.java | 5 +-
.../commons/pool2/impl/GenericObjectPool.java | 104 +-
.../apache/commons/pool2/impl/PoolImplUtils.java | 10 +-
.../java/org/apache/commons/pool2/MethodCall.java | 50 +-
.../pool2/MethodCallPoolableObjectFactory.java | 130 +-
.../apache/commons/pool2/ObjectPoolIssue326.java | 120 +-
.../java/org/apache/commons/pool2/PoolTest.java | 24 +-
.../pool2/TestBaseKeyedPoolableObjectFactory.java | 20 +-
.../apache/commons/pool2/TestBaseObjectPool.java | 210 +-
.../pool2/TestBasePoolableObjectFactory.java | 34 +-
.../apache/commons/pool2/TestKeyedObjectPool.java | 1026 ++---
.../org/apache/commons/pool2/TestObjectPool.java | 196 +-
.../org/apache/commons/pool2/TestPoolUtils.java | 780 ++--
.../org/apache/commons/pool2/VisitTracker.java | 54 +-
.../apache/commons/pool2/VisitTrackerFactory.java | 42 +-
src/test/java/org/apache/commons/pool2/Waiter.java | 122 +-
.../org/apache/commons/pool2/WaiterFactory.java | 128 +-
.../apache/commons/pool2/impl/CallStackTest.java | 4 +-
.../pool2/impl/TestAbandonedObjectPool.java | 474 +--
.../pool2/impl/TestBaseGenericObjectPool.java | 32 +-
.../pool2/impl/TestDefaultPooledObject.java | 25 +-
.../pool2/impl/TestDefaultPooledObjectInfo.java | 102 +-
.../pool2/impl/TestGenericKeyedObjectPool.java | 3988 ++++++++++----------
.../commons/pool2/impl/TestGenericObjectPool.java | 158 +-
.../impl/TestGenericObjectPoolClassLoaders.java | 84 +-
.../pool2/impl/TestLinkedBlockingDeque.java | 532 +--
.../commons/pool2/impl/TestPoolImplUtils.java | 46 +-
.../pool2/impl/TestPooledSoftReference.java | 2 +-
.../commons/pool2/impl/TestSoftRefOutOfMemory.java | 236 +-
.../pool2/impl/TestSoftReferenceObjectPool.java | 38 +-
.../impl/TestSynchronizedPooledObjectFactory.java | 34 +-
.../commons/pool2/performance/PerformanceTest.java | 164 +-
.../pool2/performance/SleepingObjectFactory.java | 52 +-
.../proxy/BaseTestProxiedKeyedObjectPool.java | 96 +-
.../pool2/proxy/BaseTestProxiedObjectPool.java | 120 +-
37 files changed, 4692 insertions(+), 4612 deletions(-)
[commons-pool] 03/04: Javadoc formatting fixes.
Posted by gg...@apache.org.
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 847d70db73bf928ad7c81bcf87899de497bbea84
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Feb 15 18:03:39 2021 -0500
Javadoc formatting fixes.
---
src/main/java/org/apache/commons/pool2/PooledObject.java | 12 ++++++------
.../org/apache/commons/pool2/impl/GenericObjectPool.java | 2 +-
.../java/org/apache/commons/pool2/impl/PoolImplUtils.java | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/main/java/org/apache/commons/pool2/PooledObject.java b/src/main/java/org/apache/commons/pool2/PooledObject.java
index 25d7736..96f726d 100644
--- a/src/main/java/org/apache/commons/pool2/PooledObject.java
+++ b/src/main/java/org/apache/commons/pool2/PooledObject.java
@@ -75,7 +75,7 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
boolean equals(Object obj);
/**
- * Gets the time in milliseconds that this object last spent in the
+ * Gets the time in milliseconds that this object last spent in the
* active state (it may still be active in which case subsequent calls will
* return an increased value).
*
@@ -94,7 +94,7 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
}
/**
- * Gets the time (using the same basis as
+ * Gets the time (using the same basis as
* {@link System#currentTimeMillis()}) that this object was created.
*
* @return The creation time for the wrapped object
@@ -102,7 +102,7 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
long getCreateTime();
/**
- * Gets the time in milliseconds that this object last spend in the
+ * Gets the time in milliseconds that this object last spend in the
* idle state (it may still be idle in which case subsequent calls will
* return an increased value).
*
@@ -111,14 +111,14 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
long getIdleTimeMillis();
/**
- * Gets the time the wrapped object was last borrowed.
+ * Gets the time the wrapped object was last borrowed.
*
* @return The time the object was last borrowed
*/
long getLastBorrowTime();
/**
- * Gets the time the wrapped object was last returned.
+ * Gets the time the wrapped object was last returned.
*
* @return The time the object was last returned
*/
@@ -136,7 +136,7 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
long getLastUsedTime();
/**
- * Gets the underlying object that is wrapped by this instance of
+ * Gets the underlying object that is wrapped by this instance of
* {@link PooledObject}.
*
* @return The wrapped object
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 d4bfc44..e602a5e 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -715,7 +715,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
}
/**
- * Gets a reference to the factory used to create, destroy and validate
+ * Gets a reference to the factory used to create, destroy and validate
* the objects used by this pool.
*
* @return the factory
diff --git a/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java b/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
index 6ba43b5..eb39b92 100644
--- a/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
+++ b/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
@@ -67,7 +67,7 @@ class PoolImplUtils {
}
/**
- * Gets the concrete type used by an implementation of an interface that uses a generic type.
+ * Gets the concrete type used by an implementation of an interface that uses a generic type.
*
* @param type
* The interface that defines a generic type
[commons-pool] 02/04: Sort members.
Posted by gg...@apache.org.
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 f2380790f74dead7fb6a6e9d5d778ef74004baf8
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Feb 15 18:03:26 2021 -0500
Sort members.
---
.../java/org/apache/commons/pool2/MethodCall.java | 50 +-
.../pool2/MethodCallPoolableObjectFactory.java | 130 +-
.../apache/commons/pool2/ObjectPoolIssue326.java | 120 +-
.../java/org/apache/commons/pool2/PoolTest.java | 24 +-
.../pool2/TestBaseKeyedPoolableObjectFactory.java | 20 +-
.../apache/commons/pool2/TestBaseObjectPool.java | 210 +-
.../pool2/TestBasePoolableObjectFactory.java | 34 +-
.../apache/commons/pool2/TestKeyedObjectPool.java | 1026 ++---
.../org/apache/commons/pool2/TestObjectPool.java | 196 +-
.../org/apache/commons/pool2/TestPoolUtils.java | 780 ++--
.../org/apache/commons/pool2/VisitTracker.java | 54 +-
.../apache/commons/pool2/VisitTrackerFactory.java | 42 +-
src/test/java/org/apache/commons/pool2/Waiter.java | 122 +-
.../org/apache/commons/pool2/WaiterFactory.java | 128 +-
.../apache/commons/pool2/impl/CallStackTest.java | 4 +-
.../pool2/impl/TestAbandonedObjectPool.java | 474 +--
.../pool2/impl/TestBaseGenericObjectPool.java | 16 +-
.../pool2/impl/TestDefaultPooledObjectInfo.java | 102 +-
.../pool2/impl/TestGenericKeyedObjectPool.java | 3988 ++++++++++----------
.../commons/pool2/impl/TestGenericObjectPool.java | 158 +-
.../impl/TestGenericObjectPoolClassLoaders.java | 84 +-
.../pool2/impl/TestLinkedBlockingDeque.java | 532 +--
.../commons/pool2/impl/TestPoolImplUtils.java | 46 +-
.../pool2/impl/TestPooledSoftReference.java | 2 +-
.../commons/pool2/impl/TestSoftRefOutOfMemory.java | 236 +-
.../pool2/impl/TestSoftReferenceObjectPool.java | 38 +-
.../impl/TestSynchronizedPooledObjectFactory.java | 34 +-
.../commons/pool2/performance/PerformanceTest.java | 164 +-
.../pool2/performance/SleepingObjectFactory.java | 52 +-
.../proxy/BaseTestProxiedKeyedObjectPool.java | 96 +-
.../pool2/proxy/BaseTestProxiedObjectPool.java | 120 +-
31 files changed, 4541 insertions(+), 4541 deletions(-)
diff --git a/src/test/java/org/apache/commons/pool2/MethodCall.java b/src/test/java/org/apache/commons/pool2/MethodCall.java
index 76a034b..9e8a3fb 100644
--- a/src/test/java/org/apache/commons/pool2/MethodCall.java
+++ b/src/test/java/org/apache/commons/pool2/MethodCall.java
@@ -33,14 +33,6 @@ public class MethodCall {
this(name, null);
}
- public MethodCall(final String name, final Object param) {
- this(name, Collections.singletonList(param));
- }
-
- public MethodCall(final String name, final Object param1, final Object param2) {
- this(name, Arrays.asList(param1, param2));
- }
-
public MethodCall(final String name, final List<Object> params) {
if (name == null) {
throw new IllegalArgumentException("name must not be null.");
@@ -53,25 +45,12 @@ public class MethodCall {
}
}
- public String getName() {
- return name;
- }
-
- public List<Object> getParams() {
- return params;
- }
-
- public Object getReturned() {
- return returned;
- }
-
- public void setReturned(final Object returned) {
- this.returned = returned;
+ public MethodCall(final String name, final Object param) {
+ this(name, Collections.singletonList(param));
}
- public MethodCall returned(final Object obj) {
- setReturned(obj);
- return this;
+ public MethodCall(final String name, final Object param1, final Object param2) {
+ this(name, Arrays.asList(param1, param2));
}
@Override
@@ -94,6 +73,18 @@ public class MethodCall {
return returned != null ? returned.equals(that.returned) : that.returned == null;
}
+ public String getName() {
+ return name;
+ }
+
+ public List<Object> getParams() {
+ return params;
+ }
+
+ public Object getReturned() {
+ return returned;
+ }
+
@Override
public int hashCode() {
int result;
@@ -103,6 +94,15 @@ public class MethodCall {
return result;
}
+ public MethodCall returned(final Object obj) {
+ setReturned(obj);
+ return this;
+ }
+
+ public void setReturned(final Object returned) {
+ this.returned = returned;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
diff --git a/src/test/java/org/apache/commons/pool2/MethodCallPoolableObjectFactory.java b/src/test/java/org/apache/commons/pool2/MethodCallPoolableObjectFactory.java
index ce69321..5621ea8 100644
--- a/src/test/java/org/apache/commons/pool2/MethodCallPoolableObjectFactory.java
+++ b/src/test/java/org/apache/commons/pool2/MethodCallPoolableObjectFactory.java
@@ -37,77 +37,54 @@ public class MethodCallPoolableObjectFactory implements PooledObjectFactory<Obje
private boolean passivateObjectFail;
private boolean destroyObjectFail;
- public void reset() {
- count = 0;
- getMethodCalls().clear();
- setMakeObjectFail(false);
- setActivateObjectFail(false);
- setValid(true);
- setValidateObjectFail(false);
- setPassivateObjectFail(false);
- setDestroyObjectFail(false);
+ @Override
+ public void activateObject(final PooledObject<Object> obj) throws Exception {
+ methodCalls.add(new MethodCall("activateObject", obj.getObject()));
+ if (activateObjectFail) {
+ throw new PrivateException("activateObject");
+ }
}
- public List<MethodCall> getMethodCalls() {
- return methodCalls;
+ @Override
+ public void destroyObject(final PooledObject<Object> obj) throws Exception {
+ methodCalls.add(new MethodCall("destroyObject", obj.getObject()));
+ if (destroyObjectFail) {
+ throw new PrivateException("destroyObject");
+ }
}
public int getCurrentCount() {
return count;
}
- public void setCurrentCount(final int count) {
- this.count = count;
- }
-
- public boolean isMakeObjectFail() {
- return makeObjectFail;
+ public List<MethodCall> getMethodCalls() {
+ return methodCalls;
}
- public void setMakeObjectFail(final boolean makeObjectFail) {
- this.makeObjectFail = makeObjectFail;
+ public boolean isActivateObjectFail() {
+ return activateObjectFail;
}
public boolean isDestroyObjectFail() {
return destroyObjectFail;
}
- public void setDestroyObjectFail(final boolean destroyObjectFail) {
- this.destroyObjectFail = destroyObjectFail;
+ public boolean isMakeObjectFail() {
+ return makeObjectFail;
}
- public boolean isValid() {
- return valid;
+ public boolean isPassivateObjectFail() {
+ return passivateObjectFail;
}
- public void setValid(final boolean valid) {
- this.valid = valid;
+ public boolean isValid() {
+ return valid;
}
public boolean isValidateObjectFail() {
return validateObjectFail;
}
- public void setValidateObjectFail(final boolean validateObjectFail) {
- this.validateObjectFail = validateObjectFail;
- }
-
- public boolean isActivateObjectFail() {
- return activateObjectFail;
- }
-
- public void setActivateObjectFail(final boolean activateObjectFail) {
- this.activateObjectFail = activateObjectFail;
- }
-
- public boolean isPassivateObjectFail() {
- return passivateObjectFail;
- }
-
- public void setPassivateObjectFail(final boolean passivateObjectFail) {
- this.passivateObjectFail = passivateObjectFail;
- }
-
@Override
public PooledObject<Object> makeObject() throws Exception {
final MethodCall call = new MethodCall("makeObject");
@@ -123,13 +100,52 @@ public class MethodCallPoolableObjectFactory implements PooledObjectFactory<Obje
}
@Override
- public void activateObject(final PooledObject<Object> obj) throws Exception {
- methodCalls.add(new MethodCall("activateObject", obj.getObject()));
- if (activateObjectFail) {
- throw new PrivateException("activateObject");
+ public void passivateObject(final PooledObject<Object> obj) throws Exception {
+ methodCalls.add(new MethodCall("passivateObject", obj.getObject()));
+ if (passivateObjectFail) {
+ throw new PrivateException("passivateObject");
}
}
+ public void reset() {
+ count = 0;
+ getMethodCalls().clear();
+ setMakeObjectFail(false);
+ setActivateObjectFail(false);
+ setValid(true);
+ 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 setValid(final boolean valid) {
+ this.valid = valid;
+ }
+
+ public void setValidateObjectFail(final boolean validateObjectFail) {
+ this.validateObjectFail = validateObjectFail;
+ }
+
@Override
public boolean validateObject(final PooledObject<Object> obj) {
final MethodCall call = new MethodCall("validateObject", obj.getObject());
@@ -141,20 +157,4 @@ public class MethodCallPoolableObjectFactory implements PooledObjectFactory<Obje
call.returned(Boolean.valueOf(r));
return r;
}
-
- @Override
- public void passivateObject(final PooledObject<Object> obj) throws Exception {
- methodCalls.add(new MethodCall("passivateObject", obj.getObject()));
- if (passivateObjectFail) {
- throw new PrivateException("passivateObject");
- }
- }
-
- @Override
- public void destroyObject(final PooledObject<Object> obj) throws Exception {
- methodCalls.add(new MethodCall("destroyObject", obj.getObject()));
- if (destroyObjectFail) {
- throw new PrivateException("destroyObject");
- }
- }
}
diff --git a/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java b/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java
index db2c2ce..da6b9fe 100644
--- a/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java
+++ b/src/test/java/org/apache/commons/pool2/ObjectPoolIssue326.java
@@ -38,6 +38,58 @@ import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
* negatively since you need to run it for a while.
*/
public final class ObjectPoolIssue326 {
+ private class ObjectFactory extends BaseKeyedPooledObjectFactory<Integer, Object> {
+ @Override
+ public Object create(final Integer s) throws Exception {
+ return new TestObject();
+ }
+
+ @Override
+ public PooledObject<Object> wrap(final Object o) {
+ return new DefaultPooledObject<>(o);
+ }
+ }
+
+ private class Task implements Callable<Object> {
+ private final GenericKeyedObjectPool<Integer, Object> m_pool;
+ private final int m_key;
+
+ Task(final GenericKeyedObjectPool<Integer, Object> pool, final int count) {
+ m_pool = pool;
+ m_key = count % 20;
+ }
+
+ private void busyWait(final long timeMillis) {
+ // busy waiting intentionally as a simple thread.sleep fails to reproduce
+ final long endTimeMillis = System.currentTimeMillis() + timeMillis;
+ while (System.currentTimeMillis() < endTimeMillis) {
+ // empty
+ }
+ }
+
+ @Override
+ public Object call() throws Exception {
+ try {
+ final Object value;
+ value = m_pool.borrowObject(m_key);
+ // don't make this too long or it won't reproduce, and don't make it zero or it
+ // won't reproduce
+ // constant low value also doesn't reproduce
+ busyWait(System.currentTimeMillis() % 4);
+ m_pool.returnObject(m_key, value);
+ return "success";
+ } catch (final NoSuchElementException e) {
+ // ignore, we've exhausted the pool
+ // not sure whether what we do here matters for reproducing
+ busyWait(System.currentTimeMillis() % 20);
+ return "exhausted";
+ }
+ }
+ }
+
+ private class TestObject {
+ }
+
public static void main(final String[] args) {
try {
new ObjectPoolIssue326().run();
@@ -46,6 +98,14 @@ public final class ObjectPoolIssue326 {
}
}
+ private List<Task> createTasks(final GenericKeyedObjectPool<Integer, Object> pool) {
+ final List<Task> tasks = new ArrayList<>();
+ for (int i = 0; i < 250; i++) {
+ tasks.add(new Task(pool, i));
+ }
+ return tasks;
+ }
+
private void run() throws Exception {
final GenericKeyedObjectPoolConfig poolConfig = new GenericKeyedObjectPoolConfig();
poolConfig.setMaxTotal(10);
@@ -94,66 +154,6 @@ public final class ObjectPoolIssue326 {
service.shutdown();
}
}
-
- private List<Task> createTasks(final GenericKeyedObjectPool<Integer, Object> pool) {
- final List<Task> tasks = new ArrayList<>();
- for (int i = 0; i < 250; i++) {
- tasks.add(new Task(pool, i));
- }
- return tasks;
- }
-
- private class ObjectFactory extends BaseKeyedPooledObjectFactory<Integer, Object> {
- @Override
- public Object create(final Integer s) throws Exception {
- return new TestObject();
- }
-
- @Override
- public PooledObject<Object> wrap(final Object o) {
- return new DefaultPooledObject<>(o);
- }
- }
-
- private class TestObject {
- }
-
- private class Task implements Callable<Object> {
- private final GenericKeyedObjectPool<Integer, Object> m_pool;
- private final int m_key;
-
- Task(final GenericKeyedObjectPool<Integer, Object> pool, final int count) {
- m_pool = pool;
- m_key = count % 20;
- }
-
- @Override
- public Object call() throws Exception {
- try {
- final Object value;
- value = m_pool.borrowObject(m_key);
- // don't make this too long or it won't reproduce, and don't make it zero or it
- // won't reproduce
- // constant low value also doesn't reproduce
- busyWait(System.currentTimeMillis() % 4);
- m_pool.returnObject(m_key, value);
- return "success";
- } catch (final NoSuchElementException e) {
- // ignore, we've exhausted the pool
- // not sure whether what we do here matters for reproducing
- busyWait(System.currentTimeMillis() % 20);
- return "exhausted";
- }
- }
-
- private void busyWait(final long timeMillis) {
- // busy waiting intentionally as a simple thread.sleep fails to reproduce
- final long endTimeMillis = System.currentTimeMillis() + timeMillis;
- while (System.currentTimeMillis() < endTimeMillis) {
- // empty
- }
- }
- }
}
/*
diff --git a/src/test/java/org/apache/commons/pool2/PoolTest.java b/src/test/java/org/apache/commons/pool2/PoolTest.java
index b115407..321490e 100644
--- a/src/test/java/org/apache/commons/pool2/PoolTest.java
+++ b/src/test/java/org/apache/commons/pool2/PoolTest.java
@@ -31,22 +31,26 @@ import java.time.Duration;
@Disabled
public class PoolTest {
- private static final CharSequence COMMONS_POOL_EVICTIONS_TIMER_THREAD_NAME = "commons-pool-EvictionTimer";
- private static final long EVICTION_PERIOD_IN_MILLIS = 100;
-
private static class Foo {
}
-
private static class PooledFooFactory implements PooledObjectFactory<Foo> {
private static final long VALIDATION_WAIT_IN_MILLIS = 1000;
@Override
+ public void activateObject(final PooledObject<Foo> pooledObject) throws Exception {
+ }
+
+ @Override
+ public void destroyObject(final PooledObject<Foo> pooledObject) throws Exception {
+ }
+
+ @Override
public PooledObject<Foo> makeObject() throws Exception {
return new DefaultPooledObject<>(new Foo());
}
@Override
- public void destroyObject(final PooledObject<Foo> pooledObject) throws Exception {
+ public void passivateObject(final PooledObject<Foo> pooledObject) throws Exception {
}
@Override
@@ -58,15 +62,11 @@ public class PoolTest {
}
return false;
}
+ }
- @Override
- public void activateObject(final PooledObject<Foo> pooledObject) throws Exception {
- }
+ private static final CharSequence COMMONS_POOL_EVICTIONS_TIMER_THREAD_NAME = "commons-pool-EvictionTimer";
- @Override
- public void passivateObject(final PooledObject<Foo> pooledObject) throws Exception {
- }
- }
+ private static final long EVICTION_PERIOD_IN_MILLIS = 100;
@Test
public void testPool() throws Exception {
diff --git a/src/test/java/org/apache/commons/pool2/TestBaseKeyedPoolableObjectFactory.java b/src/test/java/org/apache/commons/pool2/TestBaseKeyedPoolableObjectFactory.java
index ea4e668..e939cca 100644
--- a/src/test/java/org/apache/commons/pool2/TestBaseKeyedPoolableObjectFactory.java
+++ b/src/test/java/org/apache/commons/pool2/TestBaseKeyedPoolableObjectFactory.java
@@ -25,16 +25,6 @@ import org.junit.jupiter.api.Test;
*/
public class TestBaseKeyedPoolableObjectFactory {
- @Test
- public void testDefaultMethods() throws Exception {
- final KeyedPooledObjectFactory<Object,Object> factory = new TestFactory();
-
- factory.activateObject("key",null); // a no-op
- factory.passivateObject("key",null); // a no-op
- factory.destroyObject("key",null); // a no-op
- assertTrue(factory.validateObject("key",null)); // constant true
- }
-
private static class TestFactory
extends BaseKeyedPooledObjectFactory<Object,Object> {
@Override
@@ -46,4 +36,14 @@ public class TestBaseKeyedPoolableObjectFactory {
return new DefaultPooledObject<>(value);
}
}
+
+ @Test
+ public void testDefaultMethods() throws Exception {
+ final KeyedPooledObjectFactory<Object,Object> factory = new TestFactory();
+
+ factory.activateObject("key",null); // a no-op
+ factory.passivateObject("key",null); // a no-op
+ factory.destroyObject("key",null); // a no-op
+ assertTrue(factory.validateObject("key",null)); // constant true
+ }
}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java b/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java
index 9d8b920..7e777fb 100644
--- a/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/TestBaseObjectPool.java
@@ -27,28 +27,21 @@ import static org.junit.jupiter.api.Assertions.fail;
/**
*/
public class TestBaseObjectPool extends TestObjectPool {
- private ObjectPool<String> _pool = null;
-
- /**
- * @param minCapacity Ignored by this implemented. Used by sub-classes.
- *
- * @return A newly created empty pool
- */
- protected ObjectPool<String> makeEmptyPool(final int minCapacity) {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ private static class TestObjectPool extends BaseObjectPool<Object> {
+ @Override
+ public Object borrowObject() {
+ return null;
}
- throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
- }
-
- @Override
- protected ObjectPool<Object> makeEmptyPool(final PooledObjectFactory<Object> factory) {
- if (this.getClass() != TestBaseObjectPool.class) {
- fail("Subclasses of TestBaseObjectPool must reimplement this method.");
+ @Override
+ public void invalidateObject(final Object obj) {
+ }
+ @Override
+ public void returnObject(final Object obj) {
}
- throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
}
+ private ObjectPool<String> _pool = null;
+
/**
* @param n Ignored by this implemented. Used by sub-classes.
*
@@ -61,67 +54,38 @@ public class TestBaseObjectPool extends TestObjectPool {
throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
}
- protected boolean isLifo() {
+ protected boolean isFifo() {
if (this.getClass() != TestBaseObjectPool.class) {
fail("Subclasses of TestBaseObjectPool must reimplement this method.");
}
return false;
}
- protected boolean isFifo() {
+ protected boolean isLifo() {
if (this.getClass() != TestBaseObjectPool.class) {
fail("Subclasses of TestBaseObjectPool must reimplement this method.");
}
return false;
}
- // tests
- @Test
- public void testUnsupportedOperations() throws Exception {
- if (!getClass().equals(TestBaseObjectPool.class)) {
- return; // skip redundant tests
- }
- try (final ObjectPool<Object> pool = new TestObjectPool()) {
-
- assertTrue( pool.getNumIdle() < 0,"Negative expected.");
- assertTrue( pool.getNumActive() < 0,"Negative expected.");
-
- try {
- pool.clear();
- fail("Expected UnsupportedOperationException");
- } catch (final UnsupportedOperationException e) {
- // expected
- }
-
- try {
- pool.addObject();
- fail("Expected UnsupportedOperationException");
- } catch (final UnsupportedOperationException e) {
- // expected
- }
+ /**
+ * @param minCapacity Ignored by this implemented. Used by sub-classes.
+ *
+ * @return A newly created empty pool
+ */
+ protected ObjectPool<String> 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.");
}
- @Test
- public void testClose() throws Exception {
- @SuppressWarnings("resource")
- final ObjectPool<Object> pool = new TestObjectPool();
-
- pool.close();
- pool.close(); // should not error as of Pool 2.0.
- }
-
- @Test
- public void testBaseBorrow() throws Exception {
- try {
- _pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException e) {
- return; // skip this test if unsupported
+ @Override
+ protected ObjectPool<Object> makeEmptyPool(final PooledObjectFactory<Object> factory) {
+ if (this.getClass() != TestBaseObjectPool.class) {
+ fail("Subclasses of TestBaseObjectPool must reimplement this method.");
}
- assertEquals(getNthObject(0), _pool.borrowObject());
- assertEquals(getNthObject(1), _pool.borrowObject());
- assertEquals(getNthObject(2), _pool.borrowObject());
- _pool.close();
+ throw new UnsupportedOperationException("BaseObjectPool isn't a complete implementation.");
}
@Test
@@ -152,6 +116,19 @@ public class TestBaseObjectPool extends TestObjectPool {
}
@Test
+ public void testBaseBorrow() throws Exception {
+ 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() throws Exception {
try {
_pool = makeEmptyPool(3);
@@ -191,7 +168,7 @@ public class TestBaseObjectPool extends TestObjectPool {
}
@Test
- public void testBaseNumActiveNumIdle() throws Exception {
+ public void testBaseClear() throws Exception {
try {
_pool = makeEmptyPool(3);
} catch (final UnsupportedOperationException e) {
@@ -200,43 +177,38 @@ public class TestBaseObjectPool extends TestObjectPool {
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.clear();
+ assertEquals(0, _pool.getNumActive());
+ assertEquals(0, _pool.getNumIdle());
+ final Object obj2 = _pool.borrowObject();
+ assertEquals(getNthObject(2), obj2);
_pool.close();
}
@Test
- public void testBaseClear() throws Exception {
+ public void testBaseClosePool() throws Exception {
try {
_pool = makeEmptyPool(3);
- } catch (final UnsupportedOperationException e) {
+ } 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);
+ final String obj = _pool.borrowObject();
+ _pool.returnObject(obj);
+
_pool.close();
+ try {
+ _pool.borrowObject();
+ fail("Expected IllegalStateException");
+ } catch(final IllegalStateException e) {
+ // expected
+ }
}
@Test
@@ -262,34 +234,62 @@ public class TestBaseObjectPool extends TestObjectPool {
}
@Test
- public void testBaseClosePool() throws Exception {
+ public void testBaseNumActiveNumIdle() throws Exception {
try {
_pool = makeEmptyPool(3);
- } catch(final UnsupportedOperationException e) {
+ } catch (final UnsupportedOperationException e) {
return; // skip this test if unsupported
}
- final String obj = _pool.borrowObject();
- _pool.returnObject(obj);
-
+ 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();
- try {
- _pool.borrowObject();
- fail("Expected IllegalStateException");
- } catch(final IllegalStateException e) {
- // expected
- }
}
- private static class TestObjectPool extends BaseObjectPool<Object> {
- @Override
- public Object borrowObject() {
- return null;
- }
- @Override
- public void returnObject(final Object obj) {
+ @Test
+ public void testClose() throws Exception {
+ @SuppressWarnings("resource")
+ final ObjectPool<Object> pool = new TestObjectPool();
+
+ pool.close();
+ pool.close(); // should not error as of Pool 2.0.
+ }
+
+ // tests
+ @Test
+ public void testUnsupportedOperations() throws Exception {
+ if (!getClass().equals(TestBaseObjectPool.class)) {
+ return; // skip redundant tests
}
- @Override
- public void invalidateObject(final Object obj) {
+ try (final ObjectPool<Object> pool = new TestObjectPool()) {
+
+ assertTrue( pool.getNumIdle() < 0,"Negative expected.");
+ assertTrue( pool.getNumActive() < 0,"Negative expected.");
+
+ try {
+ pool.clear();
+ fail("Expected UnsupportedOperationException");
+ } catch (final UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ pool.addObject();
+ fail("Expected UnsupportedOperationException");
+ } catch (final UnsupportedOperationException e) {
+ // expected
+ }
}
}
}
diff --git a/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java b/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
index a28f0e9..97c411e 100644
--- a/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
+++ b/src/test/java/org/apache/commons/pool2/TestBasePoolableObjectFactory.java
@@ -28,6 +28,23 @@ import org.junit.jupiter.api.Test;
*/
public class TestBasePoolableObjectFactory {
+ private static class TestFactory extends BasePooledObjectFactory<AtomicInteger> {
+ @Override
+ public AtomicInteger create() throws Exception {
+ return new AtomicInteger(0);
+ }
+ @Override
+ public void destroyObject(final PooledObject<AtomicInteger> p, final DestroyMode mode){
+ if (mode.equals(DestroyMode.ABANDONED)) {
+ p.getObject().incrementAndGet();
+ }
+ }
+ @Override
+ public PooledObject<AtomicInteger> wrap(final AtomicInteger value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
@Test
public void testDefaultMethods() throws Exception {
final PooledObjectFactory<AtomicInteger> factory = new TestFactory();
@@ -55,21 +72,4 @@ public class TestBasePoolableObjectFactory {
factory.destroyObject(pooledObj, DestroyMode.ABANDONED);
assertEquals(1, obj.get());
}
-
- private static class TestFactory extends BasePooledObjectFactory<AtomicInteger> {
- @Override
- public AtomicInteger create() throws Exception {
- return new AtomicInteger(0);
- }
- @Override
- public void destroyObject(final PooledObject<AtomicInteger> p, final DestroyMode mode){
- if (mode.equals(DestroyMode.ABANDONED)) {
- p.getObject().incrementAndGet();
- }
- }
- @Override
- public PooledObject<AtomicInteger> wrap(final AtomicInteger value) {
- return new DefaultPooledObject<>(value);
- }
- }
}
diff --git a/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java b/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java
index bc4208f..e96120c 100644
--- a/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/TestKeyedObjectPool.java
@@ -33,391 +33,262 @@ import org.junit.jupiter.api.Test;
*/
public abstract class TestKeyedObjectPool {
- /**
- * Create 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 factory Factory to use to associate with the pool
- * @return The newly created empty pool
- */
- protected abstract KeyedObjectPool<Object,Object> makeEmptyPool(
- KeyedPooledObjectFactory<Object,Object> factory);
-
- protected static final String KEY = "key";
+ protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory<Object,Object> {
+ private final List<MethodCall> methodCalls = new ArrayList<>();
+ private int count = 0;
+ private boolean makeObjectFail;
+ private boolean activateObjectFail;
+ private boolean validateObjectFail;
+ private boolean passivateObjectFail;
+ private boolean destroyObjectFail;
- private KeyedObjectPool<Object,Object> _pool = null;
+ public FailingKeyedPooledObjectFactory() {
+ }
- protected abstract Object makeKey(int n);
+ @Override
+ public void activateObject(final Object key, final PooledObject<Object> obj) throws Exception {
+ methodCalls.add(new MethodCall("activateObject", key, obj.getObject()));
+ if (activateObjectFail) {
+ throw new PrivateException("activateObject");
+ }
+ }
- protected abstract boolean isFifo();
+ @Override
+ public void destroyObject(final Object key, final PooledObject<Object> obj) throws Exception {
+ methodCalls.add(new MethodCall("destroyObject", key, obj.getObject()));
+ if (destroyObjectFail) {
+ throw new PrivateException("destroyObject");
+ }
+ }
- protected abstract boolean isLifo();
+ public int getCurrentCount() {
+ return count;
+ }
- @AfterEach
- public void tearDown() throws Exception {
- _pool = null;
- }
+ public List<MethodCall> getMethodCalls() {
+ return methodCalls;
+ }
- /**
- * Create 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 KeyedObjectPool<Object,Object> makeEmptyPool(int minCapacity);
+ public boolean isActivateObjectFail() {
+ return activateObjectFail;
+ }
- /**
- * 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);
+ public boolean isDestroyObjectFail() {
+ return destroyObjectFail;
+ }
- @Test
- public void testClosedPoolBehavior() throws Exception {
- final KeyedObjectPool<Object,Object> pool;
- try {
- pool = makeEmptyPool(new TestFactory());
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
+ public boolean isMakeObjectFail() {
+ return makeObjectFail;
}
- final Object o1 = pool.borrowObject(KEY);
- final Object o2 = pool.borrowObject(KEY);
+ public boolean isPassivateObjectFail() {
+ return passivateObjectFail;
+ }
- pool.close();
+ public boolean isValidateObjectFail() {
+ return validateObjectFail;
+ }
- try {
- pool.addObject(KEY);
- fail("A closed pool must throw an IllegalStateException when addObject is called.");
- } catch (final IllegalStateException ise) {
- // expected
+ @Override
+ public PooledObject<Object> makeObject(final Object key) throws Exception {
+ 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 = new Integer(originalCount);
+ call.setReturned(obj);
+ return new DefaultPooledObject<>(obj);
}
- try {
- pool.borrowObject(KEY);
- fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
- } catch (final IllegalStateException ise) {
- // expected
+ @Override
+ public void passivateObject(final Object key, final PooledObject<Object> obj) throws Exception {
+ methodCalls.add(new MethodCall("passivateObject", key, obj.getObject()));
+ if (passivateObjectFail) {
+ throw new PrivateException("passivateObject");
+ }
}
- // 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();
- }
+ public void reset() {
+ count = 0;
+ getMethodCalls().clear();
+ setMakeObjectFail(false);
+ setActivateObjectFail(false);
+ setValidateObjectFail(false);
+ setPassivateObjectFail(false);
+ setDestroyObjectFail(false);
+ }
- // Deliberate choice to create a new object in case future unit tests check
- // for a specific object.
- private final Integer ZERO = new Integer(0);
- private final Integer ONE = new Integer(1);
+ public void setActivateObjectFail(final boolean activateObjectFail) {
+ this.activateObjectFail = activateObjectFail;
+ }
- @Test
- public void testKPOFAddObjectUsage() throws Exception {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object,Object> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
+ public void setCurrentCount(final int count) {
+ this.count = count;
}
- 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());
+ public void setDestroyObjectFail(final boolean destroyObjectFail) {
+ this.destroyObjectFail = destroyObjectFail;
+ }
- //// Test exception handling of addObject
- reset(pool, factory, expectedMethods);
+ public void setMakeObjectFail(final boolean makeObjectFail) {
+ this.makeObjectFail = makeObjectFail;
+ }
- // makeObject Exceptions should be propagated to client code from addObject
- factory.setMakeObjectFail(true);
- try {
- pool.addObject(KEY);
- fail("Expected addObject to propagate makeObject exception.");
- } catch (final PrivateException pe) {
- // expected
+ public void setPassivateObjectFail(final boolean passivateObjectFail) {
+ this.passivateObjectFail = passivateObjectFail;
}
- expectedMethods.add(new MethodCall("makeObject", KEY));
- assertEquals(expectedMethods, factory.getMethodCalls());
- clear(factory, expectedMethods);
+ public void setValidateObjectFail(final boolean validateObjectFail) {
+ this.validateObjectFail = validateObjectFail;
+ }
- // passivateObject Exceptions should be propagated to client code from addObject
- factory.setMakeObjectFail(false);
- factory.setPassivateObjectFail(true);
- try {
- pool.addObject(KEY);
- fail("Expected addObject to propagate passivateObject exception.");
- } catch (final PrivateException pe) {
- // expected
+ @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;
}
- 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() throws Exception {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object,Object> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
+ private static class TestFactory
+ extends BaseKeyedPooledObjectFactory<Object,Object> {
+ @Override
+ public Object create(final Object key) throws Exception {
+ return new Object();
}
- final List<MethodCall> expectedMethods = new ArrayList<>();
- Object obj;
-
- if (pool instanceof GenericKeyedObjectPool) {
- ((GenericKeyedObjectPool<Object,Object>) pool).setTestOnBorrow(true);
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
}
+ }
- /// Test correct behavior code paths
+ protected static final String KEY = "key";
- // 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);
+ private KeyedObjectPool<Object,Object> _pool = null;
- //// Test exception handling of borrowObject
- reset(pool, factory, expectedMethods);
+ // Deliberate choice to create a new object in case future unit tests check
+ // for a specific object.
+ private final Integer ZERO = new Integer(0);
- // 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));
- try {
- pool.borrowObject(KEY);
- fail("Expecting NoSuchElementException");
- } catch (final NoSuchElementException e) {
- //Activate should fail
- }
- // 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);
+ private final Integer ONE = new Integer(1);
- factory.setValidateObjectFail(true);
- // testOnBorrow is on, so this will throw when the newly created instance
- // fails validation
- try {
- pool.borrowObject(KEY);
- fail("Expecting NoSuchElementException");
- } catch (final NoSuchElementException ex) {
- // expected
- }
- // 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();
+ private void clear(final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) {
+ factory.getMethodCalls().clear();
+ expectedMethods.clear();
}
- @Test
- public void testKPOFReturnObjectUsages() throws Exception {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object,Object> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch(final UnsupportedOperationException uoe) {
- return; // test not supported
- }
- final List<MethodCall> expectedMethods = new ArrayList<>();
- Object obj;
+ /**
+ * 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);
- /// Test correct behavior code paths
- obj = pool.borrowObject(KEY);
- clear(factory, expectedMethods);
+ protected abstract boolean isFifo();
- // returned object should be passivated
- pool.returnObject(KEY, obj);
- expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
- assertEquals(expectedMethods, factory.getMethodCalls());
+ protected abstract boolean isLifo();
- //// Test exception handling of returnObject
- reset(pool, factory, expectedMethods);
+ /**
+ * Create 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 KeyedObjectPool<Object,Object> makeEmptyPool(int minCapacity);
+ /**
+ * Create 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 factory Factory to use to associate with the pool
+ * @return The newly created empty pool
+ */
+ protected abstract KeyedObjectPool<Object,Object> makeEmptyPool(
+ KeyedPooledObjectFactory<Object,Object> factory);
- // 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
+ protected abstract Object makeKey(int n);
- reset(pool, factory, expectedMethods);
- obj = pool.borrowObject(KEY);
+ private void reset(final KeyedObjectPool<Object,Object> pool, final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception {
+ pool.clear();
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();
+ factory.reset();
}
- @Test
- public void testKPOFInvalidateObjectUsages() throws Exception {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object,Object> 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);
- obj = pool.borrowObject(KEY);
- clear(factory, expectedMethods);
- factory.setDestroyObjectFail(true);
- try {
- pool.invalidateObject(KEY, obj);
- fail("Expecting destroy exception to propagate");
- } catch (final PrivateException ex) {
- // Expected
- }
- Thread.sleep(250); // could be defered
- TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
- assertEquals(expectedMethods, factory.getMethodCalls());
- pool.close();
+ @AfterEach
+ public void tearDown() throws Exception {
+ _pool = null;
}
@Test
- public void testKPOFClearUsages() throws Exception {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- final KeyedObjectPool<Object,Object> pool;
+ public void testBaseAddObject() throws Exception {
try {
- pool = makeEmptyPool(factory);
+ _pool = makeEmptyPool(3);
} catch(final UnsupportedOperationException uoe) {
- return; // test not supported
+ return; // skip this test if unsupported
}
- 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() throws Exception {
- final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
- KeyedObjectPool<Object, Object> pool;
+ final Object key = makeKey(0);
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> pool2 = makeEmptyPool(factory)) {
- reset(pool2, factory, expectedMethods);
- factory.setDestroyObjectFail(true);
- pool2.addObjects(KEY, 5);
+ 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 testToString() throws Exception {
- final FailingKeyedPooledObjectFactory factory =
- new FailingKeyedPooledObjectFactory();
- try (final KeyedObjectPool<Object,Object> pool = makeEmptyPool(factory)) {
- pool.toString();
+ public void testBaseBorrow() throws Exception {
+ try {
+ _pool = makeEmptyPool(3);
} catch(final UnsupportedOperationException uoe) {
- return; // test not supported
+ 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
@@ -460,20 +331,51 @@ public abstract class TestKeyedObjectPool {
}
@Test
- public void testBaseBorrow() throws Exception {
+ public void testBaseClear() throws Exception {
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");
+ 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() throws Exception {
+ 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();
}
@@ -566,237 +468,335 @@ public abstract class TestKeyedObjectPool {
}
@Test
- public void testBaseClear() throws Exception {
+ public void testClosedPoolBehavior() throws Exception {
+ final KeyedObjectPool<Object,Object> pool;
try {
- _pool = makeEmptyPool(3);
+ pool = makeEmptyPool(new TestFactory());
} catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
+ return; // test not supported
}
- 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();
+
+ final Object o1 = pool.borrowObject(KEY);
+ final Object o2 = pool.borrowObject(KEY);
+
+ pool.close();
+
+ try {
+ pool.addObject(KEY);
+ fail("A closed pool must throw an IllegalStateException when addObject is called.");
+ } catch (final IllegalStateException ise) {
+ // expected
+ }
+
+ try {
+ pool.borrowObject(KEY);
+ fail("A closed pool must throw an IllegalStateException when borrowObject is called.");
+ } catch (final IllegalStateException ise) {
+ // expected
+ }
+
+ // 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 testBaseInvalidateObject() throws Exception {
+ public void testKPOFAddObjectUsage() throws Exception {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object,Object> pool;
try {
- _pool = makeEmptyPool(3);
+ pool = makeEmptyPool(factory);
} catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
+ return; // test not supported
}
- 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();
+ 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);
+ try {
+ pool.addObject(KEY);
+ fail("Expected addObject to propagate makeObject exception.");
+ } catch (final PrivateException pe) {
+ // expected
+ }
+ 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);
+ try {
+ pool.addObject(KEY);
+ fail("Expected addObject to propagate passivateObject exception.");
+ } catch (final PrivateException pe) {
+ // expected
+ }
+ expectedMethods.add(new MethodCall("makeObject", KEY).returned(ONE));
+ expectedMethods.add(new MethodCall("passivateObject", KEY, ONE));
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.close();
}
@Test
- public void testBaseAddObject() throws Exception {
+ public void testKPOFBorrowObjectUsages() throws Exception {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object,Object> pool;
try {
- _pool = makeEmptyPool(3);
+ pool = makeEmptyPool(factory);
} catch(final UnsupportedOperationException uoe) {
- return; // skip this test if unsupported
+ return; // test not supported
}
- 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();
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
+
+ if (pool instanceof GenericKeyedObjectPool) {
+ ((GenericKeyedObjectPool<Object,Object>) pool).setTestOnBorrow(true);
}
- }
+ /// Test correct behavior code paths
- private void reset(final KeyedObjectPool<Object,Object> pool, final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception {
- pool.clear();
+ // existing idle object should be activated and validated
+ pool.addObject(KEY);
clear(factory, expectedMethods);
- factory.reset();
- }
+ 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);
- private void clear(final FailingKeyedPooledObjectFactory factory, final List<MethodCall> expectedMethods) {
- factory.getMethodCalls().clear();
- expectedMethods.clear();
- }
+ //// Test exception handling of borrowObject
+ reset(pool, factory, expectedMethods);
- private static class TestFactory
- extends BaseKeyedPooledObjectFactory<Object,Object> {
- @Override
- public Object create(final Object key) throws Exception {
- return new Object();
+ // 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
}
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
+ 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));
+ try {
+ pool.borrowObject(KEY);
+ fail("Expecting NoSuchElementException");
+ } catch (final NoSuchElementException e) {
+ //Activate should fail
}
- }
+ // 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());
- protected static class FailingKeyedPooledObjectFactory implements KeyedPooledObjectFactory<Object,Object> {
- private final List<MethodCall> methodCalls = new ArrayList<>();
- private int count = 0;
- private boolean makeObjectFail;
- private boolean activateObjectFail;
- private boolean validateObjectFail;
- private boolean passivateObjectFail;
- private boolean destroyObjectFail;
+ // when validateObject fails in borrowObject, a new object should be borrowed/created
+ reset(pool, factory, expectedMethods);
+ pool.addObject(KEY);
+ clear(factory, expectedMethods);
- public FailingKeyedPooledObjectFactory() {
+ factory.setValidateObjectFail(true);
+ // testOnBorrow is on, so this will throw when the newly created instance
+ // fails validation
+ try {
+ pool.borrowObject(KEY);
+ fail("Expecting NoSuchElementException");
+ } catch (final NoSuchElementException ex) {
+ // expected
}
+ // 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();
+ }
- public void reset() {
- count = 0;
- getMethodCalls().clear();
- setMakeObjectFail(false);
- setActivateObjectFail(false);
- setValidateObjectFail(false);
- setPassivateObjectFail(false);
- setDestroyObjectFail(false);
+ @Test
+ public void testKPOFClearUsages() throws Exception {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object,Object> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
}
+ final List<MethodCall> expectedMethods = new ArrayList<>();
- public List<MethodCall> getMethodCalls() {
- return methodCalls;
- }
+ /// Test correct behavior code paths
+ pool.addObjects(KEY, 5);
+ pool.clear();
- public int getCurrentCount() {
- return count;
- }
+ //// Test exception handling clear should swallow destroy object failures
+ reset(pool, factory, expectedMethods);
+ factory.setDestroyObjectFail(true);
+ pool.addObjects(KEY, 5);
+ pool.clear();
+ pool.close();
+ }
- public void setCurrentCount(final int count) {
- this.count = count;
- }
- public boolean isMakeObjectFail() {
- return makeObjectFail;
+ @Test
+ public void testKPOFCloseUsages() throws Exception {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ KeyedObjectPool<Object, Object> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch (final UnsupportedOperationException uoe) {
+ return; // test not supported
}
+ final List<MethodCall> expectedMethods = new ArrayList<>();
- public void setMakeObjectFail(final boolean makeObjectFail) {
- this.makeObjectFail = makeObjectFail;
- }
+ /// Test correct behavior code paths
+ pool.addObjects(KEY, 5);
+ pool.close();
- public boolean isDestroyObjectFail() {
- return destroyObjectFail;
+ //// Test exception handling close should swallow failures
+ try (final KeyedObjectPool<Object, Object> pool2 = makeEmptyPool(factory)) {
+ reset(pool2, factory, expectedMethods);
+ factory.setDestroyObjectFail(true);
+ pool2.addObjects(KEY, 5);
}
+ }
- public void setDestroyObjectFail(final boolean destroyObjectFail) {
- this.destroyObjectFail = destroyObjectFail;
+ @Test
+ public void testKPOFInvalidateObjectUsages() throws Exception {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object,Object> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
}
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
- public boolean isValidateObjectFail() {
- return validateObjectFail;
- }
+ /// Test correct behavior code paths
- public void setValidateObjectFail(final boolean validateObjectFail) {
- this.validateObjectFail = validateObjectFail;
- }
+ obj = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
- public boolean isActivateObjectFail() {
- return activateObjectFail;
- }
+ // invalidated object should be destroyed
+ pool.invalidateObject(KEY, obj);
+ expectedMethods.add(new MethodCall("destroyObject", KEY, obj));
+ assertEquals(expectedMethods, factory.getMethodCalls());
- public void setActivateObjectFail(final boolean activateObjectFail) {
- this.activateObjectFail = activateObjectFail;
+ //// Test exception handling of invalidateObject
+ reset(pool, factory, expectedMethods);
+ obj = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
+ factory.setDestroyObjectFail(true);
+ try {
+ pool.invalidateObject(KEY, obj);
+ fail("Expecting destroy exception to propagate");
+ } catch (final PrivateException ex) {
+ // Expected
}
+ Thread.sleep(250); // could be defered
+ TestObjectPool.removeDestroyObjectCall(factory.getMethodCalls());
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.close();
+ }
- public boolean isPassivateObjectFail() {
- return passivateObjectFail;
+ @Test
+ public void testKPOFReturnObjectUsages() throws Exception {
+ final FailingKeyedPooledObjectFactory factory = new FailingKeyedPooledObjectFactory();
+ final KeyedObjectPool<Object,Object> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
}
+ final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
- public void setPassivateObjectFail(final boolean passivateObjectFail) {
- this.passivateObjectFail = passivateObjectFail;
- }
+ /// Test correct behavior code paths
+ obj = pool.borrowObject(KEY);
+ clear(factory, expectedMethods);
- @Override
- public PooledObject<Object> makeObject(final Object key) throws Exception {
- 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 = new Integer(originalCount);
- call.setReturned(obj);
- return new DefaultPooledObject<>(obj);
- }
+ // returned object should be passivated
+ pool.returnObject(KEY, obj);
+ expectedMethods.add(new MethodCall("passivateObject", KEY, obj));
+ assertEquals(expectedMethods, factory.getMethodCalls());
- @Override
- public void activateObject(final Object key, final PooledObject<Object> obj) throws Exception {
- methodCalls.add(new MethodCall("activateObject", key, obj.getObject()));
- if (activateObjectFail) {
- throw new PrivateException("activateObject");
- }
- }
+ //// Test exception handling of returnObject
+ reset(pool, factory, expectedMethods);
- @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;
- }
+ // 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
- @Override
- public void passivateObject(final Object key, final PooledObject<Object> obj) throws Exception {
- methodCalls.add(new MethodCall("passivateObject", key, obj.getObject()));
- if (passivateObjectFail) {
- throw new PrivateException("passivateObject");
+ 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();
+ }
- @Override
- public void destroyObject(final Object key, final PooledObject<Object> obj) throws Exception {
- methodCalls.add(new MethodCall("destroyObject", key, obj.getObject()));
- if (destroyObjectFail) {
- throw new PrivateException("destroyObject");
- }
+ @Test
+ public void testToString() throws Exception {
+ final FailingKeyedPooledObjectFactory factory =
+ new FailingKeyedPooledObjectFactory();
+ try (final KeyedObjectPool<Object,Object> pool = makeEmptyPool(factory)) {
+ pool.toString();
+ } catch(final UnsupportedOperationException uoe) {
+ return; // test not supported
}
}
}
diff --git a/src/test/java/org/apache/commons/pool2/TestObjectPool.java b/src/test/java/org/apache/commons/pool2/TestObjectPool.java
index cd4b01c..98f8a58 100644
--- a/src/test/java/org/apache/commons/pool2/TestObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/TestObjectPool.java
@@ -35,6 +35,32 @@ import static org.junit.jupiter.api.Assertions.fail;
*/
public abstract class TestObjectPool {
+ private static void clear(final MethodCallPoolableObjectFactory factory, final List<MethodCall> expectedMethods) {
+ factory.getMethodCalls().clear();
+ expectedMethods.clear();
+ }
+
+ static void removeDestroyObjectCall(final List<MethodCall> calls) {
+ final Iterator<MethodCall> iter = calls.iterator();
+ while (iter.hasNext()) {
+ final MethodCall call = iter.next();
+ if ("destroyObject".equals(call.getName())) {
+ iter.remove();
+ }
+ }
+ }
+
+ private static void reset(final ObjectPool<Object> pool, final MethodCallPoolableObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception {
+ pool.clear();
+ clear(factory, expectedMethods);
+ factory.reset();
+ }
+ // Deliberate choice to create a new object in case future unit tests check
+ // for a specific object.
+ private final Integer ZERO = new Integer(0);
+
+ private final Integer ONE = new Integer(1);
+
/**
* Create an {@code ObjectPool} with the specified factory.
* The pool should be in a default configuration and conform to the expected
@@ -102,11 +128,6 @@ public abstract class TestObjectPool {
pool.close();
}
- // Deliberate choice to create a new object in case future unit tests check
- // for a specific object.
- private final Integer ZERO = new Integer(0);
- private final Integer ONE = new Integer(1);
-
@Test
public void testPOFAddObjectUsage() throws Exception {
final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
@@ -254,7 +275,7 @@ public abstract class TestObjectPool {
}
@Test
- public void testPOFReturnObjectUsages() throws Exception {
+ public void testPOFClearUsages() throws Exception {
final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
final ObjectPool<Object> pool;
try {
@@ -263,54 +284,44 @@ public abstract class TestObjectPool {
return; // test not supported
}
final List<MethodCall> expectedMethods = new ArrayList<>();
- Object obj;
/// Test correct behavior code paths
- obj = pool.borrowObject();
- clear(factory, expectedMethods);
-
- // returned object should be passivated
- pool.returnObject(obj);
- // StackObjectPool, SoftReferenceObjectPool also validate on return
- if (pool instanceof SoftReferenceObjectPool) {
- expectedMethods.add(new MethodCall(
- "validateObject", obj).returned(Boolean.TRUE));
- }
- expectedMethods.add(new MethodCall("passivateObject", obj));
- assertEquals(expectedMethods, factory.getMethodCalls());
+ pool.addObjects(5);
+ pool.clear();
- //// Test exception handling of returnObject
+ //// Test exception handling clear should swallow destroy object failures
reset(pool, factory, expectedMethods);
- pool.addObject();
- pool.addObject();
- pool.addObject();
- assertEquals(3, pool.getNumIdle());
- // passivateObject should swallow exceptions and not add the object to the pool
- obj = pool.borrowObject();
- pool.borrowObject();
- assertEquals(1, pool.getNumIdle());
- assertEquals(2, pool.getNumActive());
- clear(factory, expectedMethods);
- factory.setPassivateObjectFail(true);
- pool.returnObject(obj);
- // StackObjectPool, SoftReferenceObjectPool also validate on return
- if (pool instanceof SoftReferenceObjectPool) {
- expectedMethods.add(new MethodCall(
- "validateObject", obj).returned(Boolean.TRUE));
+ factory.setDestroyObjectFail(true);
+ pool.addObjects(5);
+ pool.clear();
+ pool.close();
+ }
+
+ @Test
+ public void testPOFCloseUsages() throws Exception {
+ final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
+ ObjectPool<Object> pool;
+ try {
+ pool = makeEmptyPool(factory);
+ } catch (final UnsupportedOperationException uoe) {
+ return; // test not supported
}
- expectedMethods.add(new MethodCall("passivateObject", obj));
- removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
- assertEquals(expectedMethods, factory.getMethodCalls());
- assertEquals(1, pool.getNumIdle()); // Not returned
- assertEquals(1, pool.getNumActive()); // But not in active count
+ final List<MethodCall> expectedMethods = new ArrayList<>();
- // destroyObject should swallow exceptions too
+ /// Test correct behavior code paths
+ pool.addObjects(5);
+ pool.close();
+
+
+ //// Test exception handling close should swallow failures
+ try {
+ pool = makeEmptyPool(factory);
+ } catch (final UnsupportedOperationException uoe) {
+ return; // test not supported
+ }
reset(pool, factory, expectedMethods);
- obj = pool.borrowObject();
- clear(factory, expectedMethods);
- factory.setPassivateObjectFail(true);
factory.setDestroyObjectFail(true);
- pool.returnObject(obj);
+ pool.addObjects(5);
pool.close();
}
@@ -354,7 +365,7 @@ public abstract class TestObjectPool {
}
@Test
- public void testPOFClearUsages() throws Exception {
+ public void testPOFReturnObjectUsages() throws Exception {
final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
final ObjectPool<Object> pool;
try {
@@ -363,44 +374,54 @@ public abstract class TestObjectPool {
return; // test not supported
}
final List<MethodCall> expectedMethods = new ArrayList<>();
+ Object obj;
/// Test correct behavior code paths
- pool.addObjects(5);
- pool.clear();
-
- //// Test exception handling clear should swallow destroy object failures
- reset(pool, factory, expectedMethods);
- factory.setDestroyObjectFail(true);
- pool.addObjects(5);
- pool.clear();
- pool.close();
- }
+ obj = pool.borrowObject();
+ clear(factory, expectedMethods);
- @Test
- public void testPOFCloseUsages() throws Exception {
- final MethodCallPoolableObjectFactory factory = new MethodCallPoolableObjectFactory();
- ObjectPool<Object> pool;
- try {
- pool = makeEmptyPool(factory);
- } catch (final UnsupportedOperationException uoe) {
- return; // test not supported
+ // returned object should be passivated
+ pool.returnObject(obj);
+ // StackObjectPool, SoftReferenceObjectPool also validate on return
+ if (pool instanceof SoftReferenceObjectPool) {
+ expectedMethods.add(new MethodCall(
+ "validateObject", obj).returned(Boolean.TRUE));
}
- final List<MethodCall> expectedMethods = new ArrayList<>();
-
- /// Test correct behavior code paths
- pool.addObjects(5);
- pool.close();
-
+ expectedMethods.add(new MethodCall("passivateObject", obj));
+ assertEquals(expectedMethods, factory.getMethodCalls());
- //// Test exception handling close should swallow failures
- try {
- pool = makeEmptyPool(factory);
- } catch (final UnsupportedOperationException uoe) {
- return; // test not supported
+ //// Test exception handling of returnObject
+ reset(pool, factory, expectedMethods);
+ pool.addObject();
+ pool.addObject();
+ pool.addObject();
+ assertEquals(3, pool.getNumIdle());
+ // passivateObject should swallow exceptions and not add the object to the pool
+ obj = pool.borrowObject();
+ pool.borrowObject();
+ assertEquals(1, pool.getNumIdle());
+ assertEquals(2, pool.getNumActive());
+ clear(factory, expectedMethods);
+ factory.setPassivateObjectFail(true);
+ pool.returnObject(obj);
+ // StackObjectPool, SoftReferenceObjectPool also validate on return
+ if (pool instanceof SoftReferenceObjectPool) {
+ expectedMethods.add(new MethodCall(
+ "validateObject", obj).returned(Boolean.TRUE));
}
+ expectedMethods.add(new MethodCall("passivateObject", obj));
+ removeDestroyObjectCall(factory.getMethodCalls()); // The exact timing of destroyObject is flexible here.
+ assertEquals(expectedMethods, factory.getMethodCalls());
+ assertEquals(1, pool.getNumIdle()); // Not returned
+ assertEquals(1, pool.getNumActive()); // But not in active count
+
+ // destroyObject should swallow exceptions too
reset(pool, factory, expectedMethods);
+ obj = pool.borrowObject();
+ clear(factory, expectedMethods);
+ factory.setPassivateObjectFail(true);
factory.setDestroyObjectFail(true);
- pool.addObjects(5);
+ pool.returnObject(obj);
pool.close();
}
@@ -415,25 +436,4 @@ public abstract class TestObjectPool {
pool.toString();
pool.close();
}
-
- static void removeDestroyObjectCall(final List<MethodCall> calls) {
- final Iterator<MethodCall> iter = calls.iterator();
- while (iter.hasNext()) {
- final MethodCall call = iter.next();
- if ("destroyObject".equals(call.getName())) {
- iter.remove();
- }
- }
- }
-
- private static void reset(final ObjectPool<Object> pool, final MethodCallPoolableObjectFactory factory, final List<MethodCall> expectedMethods) throws Exception {
- pool.clear();
- clear(factory, expectedMethods);
- factory.reset();
- }
-
- private static void clear(final MethodCallPoolableObjectFactory factory, final List<MethodCall> expectedMethods) {
- factory.getMethodCalls().clear();
- expectedMethods.clear();
- }
}
diff --git a/src/test/java/org/apache/commons/pool2/TestPoolUtils.java b/src/test/java/org/apache/commons/pool2/TestPoolUtils.java
index d99da9a..c38ec52 100644
--- a/src/test/java/org/apache/commons/pool2/TestPoolUtils.java
+++ b/src/test/java/org/apache/commons/pool2/TestPoolUtils.java
@@ -49,6 +49,35 @@ import org.opentest4j.AssertionFailedError;
*/
public class TestPoolUtils {
+ private static class MethodCallLogger implements InvocationHandler {
+ private final List<String> calledMethods;
+
+ MethodCallLogger(final List<String> calledMethods) {
+ this.calledMethods = calledMethods;
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+ if (calledMethods == null) {
+ return null;
+ }
+ calledMethods.add(method.getName());
+ if (boolean.class.equals(method.getReturnType())) {
+ return Boolean.FALSE;
+ } else if (int.class.equals(method.getReturnType())) {
+ return Integer.valueOf(0);
+ } else if (long.class.equals(method.getReturnType())) {
+ return Long.valueOf(0);
+ } else if (Object.class.equals(method.getReturnType())) {
+ return new Object();
+ } else if (PooledObject.class.equals(method.getReturnType())) {
+ return new DefaultPooledObject<>(new Object());
+ } else {
+ return null;
+ }
+ }
+ }
+
/** Period between checks for minIdle tests. Increase this if you happen to get too many false failures. */
private static final int CHECK_PERIOD = 300;
@@ -58,102 +87,75 @@ public class TestPoolUtils {
/** Sleep time to let the minIdle tests run CHECK_COUNT times. */
private static final int CHECK_SLEEP_PERIOD = CHECK_PERIOD * (CHECK_COUNT - 1) + CHECK_PERIOD / 2;
- @Test
- public void testCheckRethrow() {
- try {
- PoolUtils.checkRethrow(new Exception());
- } catch (final Throwable t) {
- fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError.");
- }
- try {
- PoolUtils.checkRethrow(new ThreadDeath());
- fail("PoolUtils.checkRethrow(Throwable) must rethrow ThreadDeath.");
- } catch (final ThreadDeath td) {
- // expected
- } catch (final Throwable t) {
- fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError.");
- }
- try {
- PoolUtils.checkRethrow(new InternalError()); // InternalError extends VirtualMachineError
- fail("PoolUtils.checkRethrow(Throwable) must rethrow VirtualMachineError.");
- } catch (final VirtualMachineError td) {
- // expected
- } catch (final Throwable t) {
- fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError.");
- }
+ private static <T> T createProxy(final Class<T> clazz, final InvocationHandler handler) {
+ @SuppressWarnings("unchecked")
+ final
+ T ret = (T) Proxy.newProxyInstance(
+ clazz.getClassLoader(), new Class[] { clazz }, handler);
+ return ret;
}
- @Test
- public void testJavaBeanInstantiation() {
- assertNotNull(new PoolUtils());
+ private static <T> T createProxy(final Class<T> clazz, final List<String> logger) {
+ return createProxy(clazz, new MethodCallLogger(logger));
}
- @Test
- public void testCheckMinIdleObjectPool() throws Exception {
- try {
- PoolUtils.checkMinIdle(null, 1, 1);
- fail("PoolUtils.checkMinIdle(ObjectPool,,) must not allow null pool.");
- } catch (final IllegalArgumentException iae) {
- // expected
- }
- try (@SuppressWarnings("unchecked")
- final ObjectPool<Object> pool = createProxy(ObjectPool.class, (List<String>) null)) {
- PoolUtils.checkMinIdle(pool, -1, 1);
- fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept negative min idle values.");
- } catch (final IllegalArgumentException iae) {
- // expected
- }
+ private static List<String> invokeEveryMethod(final KeyedObjectPool<Object,Object> kop) throws Exception {
+ kop.addObject(null);
+ kop.borrowObject(null);
+ kop.clear();
+ kop.clear(null);
+ kop.close();
+ kop.getNumActive();
+ kop.getNumActive(null);
+ kop.getNumIdle();
+ kop.getNumIdle(null);
+ kop.invalidateObject(null, new Object());
+ kop.returnObject(null, new Object());
+ kop.toString();
- final List<String> calledMethods = new ArrayList<>();
+ final List<String> expectedMethods = Arrays.asList("addObject", "borrowObject", "clear", "clear", "close", "getNumActive", "getNumActive",
+ "getNumIdle", "getNumIdle", "invalidateObject", "returnObject", "toString");
+ return expectedMethods;
+ }
- // Test that the minIdle check doesn't add too many idle objects
- @SuppressWarnings("unchecked")
- final PooledObjectFactory<Object> pof = createProxy(PooledObjectFactory.class, calledMethods);
- try (final ObjectPool<Object> op = new GenericObjectPool<>(pof)) {
- PoolUtils.checkMinIdle(op, 2, 100);
- Thread.sleep(1000);
- assertEquals(2, op.getNumIdle());
- }
- int makeObjectCount = 0;
- final Iterator<String> iter = calledMethods.iterator();
- while (iter.hasNext()) {
- final String methodName = iter.next();
- if ("makeObject".equals(methodName)) {
- makeObjectCount++;
- }
- }
- assertEquals( 2, makeObjectCount,"makeObject should have been called two time");
+ private static <K, V> List<String> invokeEveryMethod(final KeyedPooledObjectFactory<K, V> kpof) throws Exception {
+ kpof.activateObject(null, null);
+ kpof.destroyObject(null, null);
+ kpof.makeObject(null);
+ kpof.passivateObject(null, null);
+ kpof.validateObject(null, null);
+ kpof.toString();
- // Because this isn't deterministic and you can get false failures, try more than once.
- AssertionFailedError afe = null;
- int triesLeft = 3;
- do {
- afe = null;
- try {
- calledMethods.clear();
- try (@SuppressWarnings("unchecked")
- final ObjectPool<Object> pool = createProxy(ObjectPool.class, calledMethods)) {
- final TimerTask task = PoolUtils.checkMinIdle(pool, 1, CHECK_PERIOD); // checks minIdle immediately
+ final List<String> expectedMethods = Arrays.asList("activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString");
+ return expectedMethods;
+ }
- Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
- task.cancel();
- task.toString();
+ private static List<String> invokeEveryMethod(final ObjectPool<Object> op) throws Exception {
+ op.addObject();
+ op.borrowObject();
+ op.clear();
+ op.close();
+ op.getNumActive();
+ op.getNumIdle();
+ op.invalidateObject(new Object());
+ op.returnObject(new Object());
+ op.toString();
- final List<String> expectedMethods = new ArrayList<>();
- for (int i = 0; i < CHECK_COUNT; i++) {
- expectedMethods.add("getNumIdle");
- expectedMethods.add("addObject");
- }
- expectedMethods.add("toString");
- assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
- }
- } catch (final AssertionFailedError e) {
- afe = e;
- }
- } while (--triesLeft > 0 && afe != null);
- if (afe != null) {
- throw afe;
- }
+ final List<String> expectedMethods = Arrays.asList("addObject", "borrowObject", "clear", "close", "getNumActive", "getNumIdle", "invalidateObject",
+ "returnObject", "toString");
+ return expectedMethods;
+ }
+
+ private static <T> List<String> invokeEveryMethod(final PooledObjectFactory<T> pof) throws Exception {
+ pof.activateObject(null);
+ pof.destroyObject(null);
+ pof.makeObject();
+ pof.passivateObject(null);
+ pof.validateObject(null);
+ pof.toString();
+
+ final List<String> expectedMethods = Arrays.asList("activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString");
+ return expectedMethods;
}
@Test
@@ -237,24 +239,6 @@ public class TestPoolUtils {
}
@Test
- public void testCheckMinIdleKeyedObjectPoolKeysNulls() throws Exception {
- try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<Object,Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null)) {
- PoolUtils.checkMinIdle(pool, (Collection<?>) null, 1, 1);
- fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must not accept null keys.");
- } catch (final IllegalArgumentException iae) {
- // expected
- }
-
- try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<Object,Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null)) {
- PoolUtils.checkMinIdle(pool, (Collection<?>) Collections.emptyList(), 1, 1);
- } catch (final IllegalArgumentException iae) {
- fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must accept empty lists.");
- }
- }
-
- @Test
public void testCheckMinIdleKeyedObjectPoolKeys() throws Exception {
// Because this isn't deterministic and you can get false failures, try more than once.
AssertionFailedError afe = null;
@@ -290,184 +274,149 @@ public class TestPoolUtils {
}
}
- @SuppressWarnings("deprecation")
@Test
- public void testPrefillObjectPool() throws Exception {
- try {
- PoolUtils.prefill(null, 1);
- fail("PoolUtils.prefill(ObjectPool,int) must not allow null pool.");
+ public void testCheckMinIdleKeyedObjectPoolKeysNulls() throws Exception {
+ try (@SuppressWarnings("unchecked")
+ final KeyedObjectPool<Object,Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null)) {
+ PoolUtils.checkMinIdle(pool, (Collection<?>) null, 1, 1);
+ fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must not accept null keys.");
} catch (final IllegalArgumentException iae) {
// expected
}
- final List<String> calledMethods = new ArrayList<>();
try (@SuppressWarnings("unchecked")
- final ObjectPool<Object> pool = createProxy(ObjectPool.class, calledMethods)) {
-
- PoolUtils.prefill(pool, 0);
- final List<String> expectedMethods = new ArrayList<>();
- expectedMethods.add("addObjects");
- assertEquals(expectedMethods, calledMethods);
-
- calledMethods.clear();
- PoolUtils.prefill(pool, 3);
- assertEquals(expectedMethods, calledMethods);
+ final KeyedObjectPool<Object,Object> pool = createProxy(KeyedObjectPool.class, (List<String>)null)) {
+ PoolUtils.checkMinIdle(pool, (Collection<?>) Collections.emptyList(), 1, 1);
+ } catch (final IllegalArgumentException iae) {
+ fail("PoolUtils.checkMinIdle(KeyedObjectPool,Collection,int,long) must accept empty lists.");
}
}
- @SuppressWarnings("deprecation")
@Test
- public void testPrefillKeyedObjectPool() throws Exception {
+ public void testCheckMinIdleObjectPool() throws Exception {
try {
- PoolUtils.prefill(null, new Object(), 1);
- fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null pool.");
+ PoolUtils.checkMinIdle(null, 1, 1);
+ fail("PoolUtils.checkMinIdle(ObjectPool,,) must not allow null pool.");
} catch (final IllegalArgumentException iae) {
// expected
}
- try (final KeyedObjectPool<Object, String> pool = new GenericKeyedObjectPool<>(
- new TestGenericKeyedObjectPool.SimpleFactory<>())) {
- PoolUtils.prefill(pool, (Object) null, 1);
- fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null key.");
+ try (@SuppressWarnings("unchecked")
+ final ObjectPool<Object> pool = createProxy(ObjectPool.class, (List<String>) null)) {
+ PoolUtils.checkMinIdle(pool, -1, 1);
+ fail("PoolUtils.checkMinIdle(ObjectPool,,) must not accept negative min idle values.");
} catch (final IllegalArgumentException iae) {
// expected
}
final List<String> calledMethods = new ArrayList<>();
- try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, calledMethods)) {
-
- PoolUtils.prefill(pool, new Object(), 0);
- final List<String> expectedMethods = new ArrayList<>();
- expectedMethods.add("addObjects");
- assertEquals(expectedMethods, calledMethods);
- calledMethods.clear();
- PoolUtils.prefill(pool, new Object(), 3);
- assertEquals(expectedMethods, calledMethods);
+ // Test that the minIdle check doesn't add too many idle objects
+ @SuppressWarnings("unchecked")
+ final PooledObjectFactory<Object> pof = createProxy(PooledObjectFactory.class, calledMethods);
+ try (final ObjectPool<Object> op = new GenericObjectPool<>(pof)) {
+ PoolUtils.checkMinIdle(op, 2, 100);
+ Thread.sleep(1000);
+ assertEquals(2, op.getNumIdle());
}
- }
-
- @SuppressWarnings("deprecation")
- @Test
- public void testPrefillKeyedObjectPoolCollection() throws Exception {
- try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<String, String> pool = createProxy(KeyedObjectPool.class, (List<String>) null)) {
- PoolUtils.prefill(pool, (Collection<String>) null, 1);
- fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not accept null keys.");
- } catch (final IllegalArgumentException iae) {
- // expected
+ int makeObjectCount = 0;
+ final Iterator<String> iter = calledMethods.iterator();
+ while (iter.hasNext()) {
+ final String methodName = iter.next();
+ if ("makeObject".equals(methodName)) {
+ makeObjectCount++;
+ }
}
+ assertEquals( 2, makeObjectCount,"makeObject should have been called two time");
- final List<String> calledMethods = new ArrayList<>();
- try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<String, Object> pool = createProxy(KeyedObjectPool.class, calledMethods)) {
+ // Because this isn't deterministic and you can get false failures, try more than once.
+ AssertionFailedError afe = null;
+ int triesLeft = 3;
+ do {
+ afe = null;
+ try {
+ calledMethods.clear();
+ try (@SuppressWarnings("unchecked")
+ final ObjectPool<Object> pool = createProxy(ObjectPool.class, calledMethods)) {
+ final TimerTask task = PoolUtils.checkMinIdle(pool, 1, CHECK_PERIOD); // checks minIdle immediately
- final Set<String> keys = new HashSet<>();
- PoolUtils.prefill(pool, keys, 0);
- final List<String> expectedMethods = new ArrayList<>();
- expectedMethods.add("addObjects");
- assertEquals(expectedMethods, calledMethods);
+ Thread.sleep(CHECK_SLEEP_PERIOD); // will check CHECK_COUNT more times.
+ task.cancel();
+ task.toString();
- calledMethods.clear();
- keys.add("one");
- keys.add("two");
- keys.add("three");
- final int count = 3;
- PoolUtils.prefill(pool, keys, count);
- assertEquals(expectedMethods, calledMethods);
+ final List<String> expectedMethods = new ArrayList<>();
+ for (int i = 0; i < CHECK_COUNT; i++) {
+ expectedMethods.add("getNumIdle");
+ expectedMethods.add("addObject");
+ }
+ expectedMethods.add("toString");
+ assertEquals(expectedMethods, calledMethods); // may fail because of the thread scheduler
+ }
+ } catch (final AssertionFailedError e) {
+ afe = e;
+ }
+ } while (--triesLeft > 0 && afe != null);
+ if (afe != null) {
+ throw afe;
}
}
@Test
- public void testSynchronizedPoolObjectPool() throws Exception {
- try (final ObjectPool<Object> synchronizedPool = PoolUtils.synchronizedPool((ObjectPool<Object>) null)) {
- fail("PoolUtils.synchronizedPool(ObjectPool) must not allow a null pool.");
- } catch (final IllegalArgumentException iae) {
- // expected
- }
-
- final List<String> calledMethods = new ArrayList<>();
- try (@SuppressWarnings("unchecked")
- final ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods);
- final ObjectPool<Object> sop = PoolUtils.synchronizedPool(op)) {
- final List<String> expectedMethods = invokeEveryMethod(sop);
- assertEquals(expectedMethods, calledMethods);
-
- // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+ public void testCheckRethrow() {
+ try {
+ PoolUtils.checkRethrow(new Exception());
+ } catch (final Throwable t) {
+ fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError.");
}
- }
-
- @Test
- public void testSynchronizedPoolKeyedObjectPool() throws Exception {
- try (final KeyedObjectPool<Object, Object> synchronizedPool = PoolUtils
- .synchronizedPool((KeyedObjectPool<Object, Object>) null)) {
- fail("PoolUtils.synchronizedPool(KeyedObjectPool) must not allow a null pool.");
- } catch (final IllegalArgumentException iae) {
+ try {
+ PoolUtils.checkRethrow(new ThreadDeath());
+ fail("PoolUtils.checkRethrow(Throwable) must rethrow ThreadDeath.");
+ } catch (final ThreadDeath td) {
// expected
+ } catch (final Throwable t) {
+ fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError.");
}
-
- final List<String> calledMethods = new ArrayList<>();
- try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<Object, Object> kop = createProxy(KeyedObjectPool.class, calledMethods);
- final KeyedObjectPool<Object, Object> skop = PoolUtils.synchronizedPool(kop)) {
- final List<String> expectedMethods = invokeEveryMethod(skop);
- assertEquals(expectedMethods, calledMethods);
+ try {
+ PoolUtils.checkRethrow(new InternalError()); // InternalError extends VirtualMachineError
+ fail("PoolUtils.checkRethrow(Throwable) must rethrow VirtualMachineError.");
+ } catch (final VirtualMachineError td) {
+ // expected
+ } catch (final Throwable t) {
+ fail("PoolUtils.checkRethrow(Throwable) must rethrow only ThreadDeath and VirtualMachineError.");
}
-
- // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
}
@Test
- public void testSynchronizedPoolableFactoryPoolableObjectFactory() throws Exception {
- try {
- PoolUtils.synchronizedPooledFactory((PooledObjectFactory<Object>) null);
- fail("PoolUtils.synchronizedPoolableFactory(PoolableObjectFactory) must not allow a null factory.");
- } catch (final IllegalArgumentException iae) {
- // expected
+ public void testErodingObjectPoolDefaultFactor() {
+ try (@SuppressWarnings("unchecked")
+ final ObjectPool<Object> internalPool = createProxy(ObjectPool.class, (arg0, arg1, arg2) -> null);
+ final ObjectPool<Object> pool = PoolUtils.erodingPool(internalPool)) {
+ final String expectedToString = "ErodingObjectPool{factor=ErodingFactor{factor=1.0, idleHighWaterMark=1}, pool=" +
+ internalPool + "}";
+ // The factor is not exposed, but will be printed in the toString() method
+ // In this case since we didn't pass one, the default 1.0f will be printed
+ assertEquals(expectedToString, pool.toString());
}
-
- final List<String> calledMethods = new ArrayList<>();
- @SuppressWarnings("unchecked")
- final PooledObjectFactory<Object> pof = createProxy(PooledObjectFactory.class, calledMethods);
-
- final PooledObjectFactory<Object> spof = PoolUtils.synchronizedPooledFactory(pof);
- final List<String> expectedMethods = invokeEveryMethod(spof);
- assertEquals(expectedMethods, calledMethods);
-
- // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
}
@Test
- public void testSynchronizedPoolableFactoryKeyedPoolableObjectFactory() throws Exception {
- try {
- PoolUtils.synchronizedKeyedPooledFactory((KeyedPooledObjectFactory<Object, Object>) null);
- fail("PoolUtils.synchronizedPoolableFactory(KeyedPoolableObjectFactory) must not allow a null factory.");
+ public void testErodingPerKeyKeyedObjectPool() throws Exception {
+ try (final KeyedObjectPool<Object, Object> erodingPool = PoolUtils
+ .erodingPool((KeyedObjectPool<Object, Object>) null, 1f, true)) {
+ fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool.");
} catch (final IllegalArgumentException iae) {
// expected
}
- final List<String> calledMethods = new ArrayList<>();
- @SuppressWarnings("unchecked")
- final KeyedPooledObjectFactory<Object, Object> kpof = createProxy(KeyedPooledObjectFactory.class,
- calledMethods);
-
- final KeyedPooledObjectFactory<Object, Object> skpof = PoolUtils.synchronizedKeyedPooledFactory(kpof);
- final List<String> expectedMethods = invokeEveryMethod(skpof);
- assertEquals(expectedMethods, calledMethods);
-
- // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
- }
-
- @Test
- public void testErodingPoolObjectPool() throws Exception {
- try (final ObjectPool<Object> erodingPool = PoolUtils.erodingPool((ObjectPool<Object>) null)) {
- fail("PoolUtils.erodingPool(ObjectPool) must not allow a null pool.");
+ try (final KeyedObjectPool<Object, Object> erodingPool = PoolUtils
+ .erodingPool((KeyedObjectPool<Object, Object>) null, 0f, true)) {
+ fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor.");
} catch (final IllegalArgumentException iae) {
// expected
}
- try (final ObjectPool<Object> erodingPool = PoolUtils.erodingPool((ObjectPool<Object>) null, 1f)) {
- fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a null pool.");
+ try (final KeyedObjectPool<Object, Object> erodingPool = PoolUtils
+ .erodingPool((KeyedObjectPool<Object, Object>) null, 1f, true)) {
+ fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool.");
} catch (final IllegalArgumentException iae) {
// expected
}
@@ -485,46 +434,33 @@ public class TestPoolUtils {
}
};
- try (@SuppressWarnings({ "unchecked" })
- final ObjectPool<?> o = PoolUtils.erodingPool(createProxy(ObjectPool.class, handler), -1f)) {
- fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
- } catch (final IllegalArgumentException iae) {
- // expected
- }
-
// If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
final float factor = 0.01f; // about ~9 seconds until first discard
- final List<String> expectedMethods = new ArrayList<>();
try (@SuppressWarnings("unchecked")
- final ObjectPool<Object> pool = PoolUtils.erodingPool(createProxy(ObjectPool.class, handler), factor)) {
+ final KeyedObjectPool<Object, Object> pool = PoolUtils.erodingPool(createProxy(KeyedObjectPool.class, handler),
+ factor, true)) {
+ final List<String> expectedMethods = new ArrayList<>();
assertEquals(expectedMethods, calledMethods);
- pool.addObject();
- expectedMethods.add("addObject");
+ final Object key = "key";
- Object o = pool.borrowObject();
+ Object o = pool.borrowObject(key);
expectedMethods.add("borrowObject");
assertEquals(expectedMethods, calledMethods);
- pool.returnObject(o);
+ pool.returnObject(key, o);
expectedMethods.add("returnObject");
assertEquals(expectedMethods, calledMethods);
- // the invocation handler always returns 1
- assertEquals(1, pool.getNumActive());
- expectedMethods.add("getNumActive");
- assertEquals(1, pool.getNumIdle());
- expectedMethods.add("getNumIdle");
-
for (int i = 0; i < 5; i++) {
- o = pool.borrowObject();
+ o = pool.borrowObject(key);
expectedMethods.add("borrowObject");
Thread.sleep(50);
- pool.returnObject(o);
+ pool.returnObject(key, o);
expectedMethods.add("returnObject");
assertEquals(expectedMethods, calledMethods);
@@ -535,27 +471,14 @@ public class TestPoolUtils {
Thread.sleep(10000); // 10 seconds
- o = pool.borrowObject();
+ o = pool.borrowObject(key);
expectedMethods.add("borrowObject");
- pool.returnObject(o);
+ pool.returnObject(key, o);
expectedMethods.add("getNumIdle");
expectedMethods.add("invalidateObject");
- pool.clear();
- }
- expectedMethods.add("clear");
- expectedMethods.add("close");
- assertEquals(expectedMethods, calledMethods);
- }
+ assertEquals(expectedMethods, calledMethods);
- @Test
- public void testErodingObjectPoolDefaultFactor() {
- try (@SuppressWarnings("unchecked")
- final ObjectPool<Object> internalPool = createProxy(ObjectPool.class, (arg0, arg1, arg2) -> null);
- final ObjectPool<Object> pool = PoolUtils.erodingPool(internalPool)) {
- final String expectedToString = "ErodingObjectPool{factor=ErodingFactor{factor=1.0, idleHighWaterMark=1}, pool=" +
- internalPool + "}";
- // The factor is not exposed, but will be printed in the toString() method
- // In this case since we didn't pass one, the default 1.0f will be printed
+ final String expectedToString = "ErodingPerKeyKeyedObjectPool{factor=" + factor + ", keyedPool=null}";
assertEquals(expectedToString, pool.toString());
}
}
@@ -683,24 +606,15 @@ public class TestPoolUtils {
}
@Test
- public void testErodingPerKeyKeyedObjectPool() throws Exception {
- try (final KeyedObjectPool<Object, Object> erodingPool = PoolUtils
- .erodingPool((KeyedObjectPool<Object, Object>) null, 1f, true)) {
- fail("PoolUtils.erodingPool(KeyedObjectPool) must not allow a null pool.");
- } catch (final IllegalArgumentException iae) {
- // expected
- }
-
- try (final KeyedObjectPool<Object, Object> erodingPool = PoolUtils
- .erodingPool((KeyedObjectPool<Object, Object>) null, 0f, true)) {
- fail("PoolUtils.erodingPool(ObjectPool, float, boolean) must not allow a non-positive factor.");
+ public void testErodingPoolObjectPool() throws Exception {
+ try (final ObjectPool<Object> erodingPool = PoolUtils.erodingPool((ObjectPool<Object>) null)) {
+ fail("PoolUtils.erodingPool(ObjectPool) must not allow a null pool.");
} catch (final IllegalArgumentException iae) {
// expected
}
- try (final KeyedObjectPool<Object, Object> erodingPool = PoolUtils
- .erodingPool((KeyedObjectPool<Object, Object>) null, 1f, true)) {
- fail("PoolUtils.erodingPool(KeyedObjectPool, float, boolean) must not allow a null pool.");
+ try (final ObjectPool<Object> erodingPool = PoolUtils.erodingPool((ObjectPool<Object>) null, 1f)) {
+ fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a null pool.");
} catch (final IllegalArgumentException iae) {
// expected
}
@@ -718,33 +632,46 @@ public class TestPoolUtils {
}
};
+ try (@SuppressWarnings({ "unchecked" })
+ final ObjectPool<?> o = PoolUtils.erodingPool(createProxy(ObjectPool.class, handler), -1f)) {
+ fail("PoolUtils.erodingPool(ObjectPool, float) must not allow a non-positive factor.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
+
// If the logic behind PoolUtils.erodingPool changes then this will need to be tweaked.
final float factor = 0.01f; // about ~9 seconds until first discard
+ final List<String> expectedMethods = new ArrayList<>();
try (@SuppressWarnings("unchecked")
- final KeyedObjectPool<Object, Object> pool = PoolUtils.erodingPool(createProxy(KeyedObjectPool.class, handler),
- factor, true)) {
+ final ObjectPool<Object> pool = PoolUtils.erodingPool(createProxy(ObjectPool.class, handler), factor)) {
- final List<String> expectedMethods = new ArrayList<>();
assertEquals(expectedMethods, calledMethods);
- final Object key = "key";
+ pool.addObject();
+ expectedMethods.add("addObject");
- Object o = pool.borrowObject(key);
+ Object o = pool.borrowObject();
expectedMethods.add("borrowObject");
assertEquals(expectedMethods, calledMethods);
- pool.returnObject(key, o);
+ pool.returnObject(o);
expectedMethods.add("returnObject");
assertEquals(expectedMethods, calledMethods);
- for (int i = 0; i < 5; i++) {
- o = pool.borrowObject(key);
- expectedMethods.add("borrowObject");
-
- Thread.sleep(50);
+ // the invocation handler always returns 1
+ assertEquals(1, pool.getNumActive());
+ expectedMethods.add("getNumActive");
+ assertEquals(1, pool.getNumIdle());
+ expectedMethods.add("getNumIdle");
- pool.returnObject(key, o);
+ for (int i = 0; i < 5; i++) {
+ o = pool.borrowObject();
+ expectedMethods.add("borrowObject");
+
+ Thread.sleep(50);
+
+ pool.returnObject(o);
expectedMethods.add("returnObject");
assertEquals(expectedMethods, calledMethods);
@@ -755,125 +682,198 @@ public class TestPoolUtils {
Thread.sleep(10000); // 10 seconds
- o = pool.borrowObject(key);
+ o = pool.borrowObject();
expectedMethods.add("borrowObject");
- pool.returnObject(key, o);
+ pool.returnObject(o);
expectedMethods.add("getNumIdle");
expectedMethods.add("invalidateObject");
- assertEquals(expectedMethods, calledMethods);
-
- final String expectedToString = "ErodingPerKeyKeyedObjectPool{factor=" + factor + ", keyedPool=null}";
- assertEquals(expectedToString, pool.toString());
+ pool.clear();
}
+ expectedMethods.add("clear");
+ expectedMethods.add("close");
+ assertEquals(expectedMethods, calledMethods);
}
- /**
- * Tests the {@link PoolUtils} timer holder.
- */
@Test
- public void testTimerHolder() {
- final PoolUtils.TimerHolder h = new PoolUtils.TimerHolder();
- assertNotNull(h);
- assertNotNull(PoolUtils.TimerHolder.MIN_IDLE_TIMER);
+ public void testJavaBeanInstantiation() {
+ assertNotNull(new PoolUtils());
}
- private static List<String> invokeEveryMethod(final ObjectPool<Object> op) throws Exception {
- op.addObject();
- op.borrowObject();
- op.clear();
- op.close();
- op.getNumActive();
- op.getNumIdle();
- op.invalidateObject(new Object());
- op.returnObject(new Object());
- op.toString();
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testPrefillKeyedObjectPool() throws Exception {
+ try {
+ PoolUtils.prefill(null, new Object(), 1);
+ fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null pool.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
+ try (final KeyedObjectPool<Object, String> pool = new GenericKeyedObjectPool<>(
+ new TestGenericKeyedObjectPool.SimpleFactory<>())) {
+ PoolUtils.prefill(pool, (Object) null, 1);
+ fail("PoolUtils.prefill(KeyedObjectPool,Object,int) must not accept null key.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
- final List<String> expectedMethods = Arrays.asList("addObject", "borrowObject", "clear", "close", "getNumActive", "getNumIdle", "invalidateObject",
- "returnObject", "toString");
- return expectedMethods;
- }
+ final List<String> calledMethods = new ArrayList<>();
+ try (@SuppressWarnings("unchecked")
+ final KeyedObjectPool<Object, Object> pool = createProxy(KeyedObjectPool.class, calledMethods)) {
- private static List<String> invokeEveryMethod(final KeyedObjectPool<Object,Object> kop) throws Exception {
- kop.addObject(null);
- kop.borrowObject(null);
- kop.clear();
- kop.clear(null);
- kop.close();
- kop.getNumActive();
- kop.getNumActive(null);
- kop.getNumIdle();
- kop.getNumIdle(null);
- kop.invalidateObject(null, new Object());
- kop.returnObject(null, new Object());
- kop.toString();
+ PoolUtils.prefill(pool, new Object(), 0);
+ final List<String> expectedMethods = new ArrayList<>();
+ expectedMethods.add("addObjects");
+ assertEquals(expectedMethods, calledMethods);
- final List<String> expectedMethods = Arrays.asList("addObject", "borrowObject", "clear", "clear", "close", "getNumActive", "getNumActive",
- "getNumIdle", "getNumIdle", "invalidateObject", "returnObject", "toString");
- return expectedMethods;
+ calledMethods.clear();
+ PoolUtils.prefill(pool, new Object(), 3);
+ assertEquals(expectedMethods, calledMethods);
+ }
}
- private static <T> List<String> invokeEveryMethod(final PooledObjectFactory<T> pof) throws Exception {
- pof.activateObject(null);
- pof.destroyObject(null);
- pof.makeObject();
- pof.passivateObject(null);
- pof.validateObject(null);
- pof.toString();
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testPrefillKeyedObjectPoolCollection() throws Exception {
+ try (@SuppressWarnings("unchecked")
+ final KeyedObjectPool<String, String> pool = createProxy(KeyedObjectPool.class, (List<String>) null)) {
+ PoolUtils.prefill(pool, (Collection<String>) null, 1);
+ fail("PoolUtils.prefill(KeyedObjectPool,Collection,int) must not accept null keys.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
- final List<String> expectedMethods = Arrays.asList("activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString");
- return expectedMethods;
+ final List<String> calledMethods = new ArrayList<>();
+ try (@SuppressWarnings("unchecked")
+ final KeyedObjectPool<String, Object> pool = createProxy(KeyedObjectPool.class, calledMethods)) {
+
+ final Set<String> keys = new HashSet<>();
+ PoolUtils.prefill(pool, keys, 0);
+ final List<String> expectedMethods = new ArrayList<>();
+ expectedMethods.add("addObjects");
+ assertEquals(expectedMethods, calledMethods);
+
+ calledMethods.clear();
+ keys.add("one");
+ keys.add("two");
+ keys.add("three");
+ final int count = 3;
+ PoolUtils.prefill(pool, keys, count);
+ assertEquals(expectedMethods, calledMethods);
+ }
}
- private static <K, V> List<String> invokeEveryMethod(final KeyedPooledObjectFactory<K, V> kpof) throws Exception {
- kpof.activateObject(null, null);
- kpof.destroyObject(null, null);
- kpof.makeObject(null);
- kpof.passivateObject(null, null);
- kpof.validateObject(null, null);
- kpof.toString();
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testPrefillObjectPool() throws Exception {
+ try {
+ PoolUtils.prefill(null, 1);
+ fail("PoolUtils.prefill(ObjectPool,int) must not allow null pool.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
- final List<String> expectedMethods = Arrays.asList("activateObject", "destroyObject", "makeObject", "passivateObject", "validateObject", "toString");
- return expectedMethods;
+ final List<String> calledMethods = new ArrayList<>();
+ try (@SuppressWarnings("unchecked")
+ final ObjectPool<Object> pool = createProxy(ObjectPool.class, calledMethods)) {
+
+ PoolUtils.prefill(pool, 0);
+ final List<String> expectedMethods = new ArrayList<>();
+ expectedMethods.add("addObjects");
+ assertEquals(expectedMethods, calledMethods);
+
+ calledMethods.clear();
+ PoolUtils.prefill(pool, 3);
+ assertEquals(expectedMethods, calledMethods);
+ }
}
- private static <T> T createProxy(final Class<T> clazz, final List<String> logger) {
- return createProxy(clazz, new MethodCallLogger(logger));
+ @Test
+ public void testSynchronizedPoolableFactoryKeyedPoolableObjectFactory() throws Exception {
+ try {
+ PoolUtils.synchronizedKeyedPooledFactory((KeyedPooledObjectFactory<Object, Object>) null);
+ fail("PoolUtils.synchronizedPoolableFactory(KeyedPoolableObjectFactory) must not allow a null factory.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
+
+ final List<String> calledMethods = new ArrayList<>();
+ @SuppressWarnings("unchecked")
+ final KeyedPooledObjectFactory<Object, Object> kpof = createProxy(KeyedPooledObjectFactory.class,
+ calledMethods);
+
+ final KeyedPooledObjectFactory<Object, Object> skpof = PoolUtils.synchronizedKeyedPooledFactory(kpof);
+ final List<String> expectedMethods = invokeEveryMethod(skpof);
+ assertEquals(expectedMethods, calledMethods);
+
+ // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
}
- private static <T> T createProxy(final Class<T> clazz, final InvocationHandler handler) {
+ @Test
+ public void testSynchronizedPoolableFactoryPoolableObjectFactory() throws Exception {
+ try {
+ PoolUtils.synchronizedPooledFactory((PooledObjectFactory<Object>) null);
+ fail("PoolUtils.synchronizedPoolableFactory(PoolableObjectFactory) must not allow a null factory.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
+
+ final List<String> calledMethods = new ArrayList<>();
@SuppressWarnings("unchecked")
- final
- T ret = (T) Proxy.newProxyInstance(
- clazz.getClassLoader(), new Class[] { clazz }, handler);
- return ret;
+ final PooledObjectFactory<Object> pof = createProxy(PooledObjectFactory.class, calledMethods);
+
+ final PooledObjectFactory<Object> spof = PoolUtils.synchronizedPooledFactory(pof);
+ final List<String> expectedMethods = invokeEveryMethod(spof);
+ assertEquals(expectedMethods, calledMethods);
+
+ // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
}
- private static class MethodCallLogger implements InvocationHandler {
- private final List<String> calledMethods;
+ @Test
+ public void testSynchronizedPoolKeyedObjectPool() throws Exception {
+ try (final KeyedObjectPool<Object, Object> synchronizedPool = PoolUtils
+ .synchronizedPool((KeyedObjectPool<Object, Object>) null)) {
+ fail("PoolUtils.synchronizedPool(KeyedObjectPool) must not allow a null pool.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
+ }
- MethodCallLogger(final List<String> calledMethods) {
- this.calledMethods = calledMethods;
+ final List<String> calledMethods = new ArrayList<>();
+ try (@SuppressWarnings("unchecked")
+ final KeyedObjectPool<Object, Object> kop = createProxy(KeyedObjectPool.class, calledMethods);
+ final KeyedObjectPool<Object, Object> skop = PoolUtils.synchronizedPool(kop)) {
+ final List<String> expectedMethods = invokeEveryMethod(skop);
+ assertEquals(expectedMethods, calledMethods);
}
- @Override
- public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
- if (calledMethods == null) {
- return null;
- }
- calledMethods.add(method.getName());
- if (boolean.class.equals(method.getReturnType())) {
- return Boolean.FALSE;
- } else if (int.class.equals(method.getReturnType())) {
- return Integer.valueOf(0);
- } else if (long.class.equals(method.getReturnType())) {
- return Long.valueOf(0);
- } else if (Object.class.equals(method.getReturnType())) {
- return new Object();
- } else if (PooledObject.class.equals(method.getReturnType())) {
- return new DefaultPooledObject<>(new Object());
- } else {
- return null;
- }
+ // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+ }
+
+ @Test
+ public void testSynchronizedPoolObjectPool() throws Exception {
+ try (final ObjectPool<Object> synchronizedPool = PoolUtils.synchronizedPool((ObjectPool<Object>) null)) {
+ fail("PoolUtils.synchronizedPool(ObjectPool) must not allow a null pool.");
+ } catch (final IllegalArgumentException iae) {
+ // expected
}
+
+ final List<String> calledMethods = new ArrayList<>();
+ try (@SuppressWarnings("unchecked")
+ final ObjectPool<Object> op = createProxy(ObjectPool.class, calledMethods);
+ final ObjectPool<Object> sop = PoolUtils.synchronizedPool(op)) {
+ final List<String> expectedMethods = invokeEveryMethod(sop);
+ assertEquals(expectedMethods, calledMethods);
+
+ // TODO: Anyone feel motivated to construct a test that verifies proper synchronization?
+ }
+ }
+
+ /**
+ * Tests the {@link PoolUtils} timer holder.
+ */
+ @Test
+ public void testTimerHolder() {
+ final PoolUtils.TimerHolder h = new PoolUtils.TimerHolder();
+ assertNotNull(h);
+ assertNotNull(PoolUtils.TimerHolder.MIN_IDLE_TIMER);
}
}
diff --git a/src/test/java/org/apache/commons/pool2/VisitTracker.java b/src/test/java/org/apache/commons/pool2/VisitTracker.java
index 4afa2cc..5de5a87 100644
--- a/src/test/java/org/apache/commons/pool2/VisitTracker.java
+++ b/src/test/java/org/apache/commons/pool2/VisitTracker.java
@@ -44,58 +44,58 @@ public class VisitTracker<K> {
reset();
}
- public boolean validate() {
- if (destroyed) {
- fail("attempted to validate a destroyed object");
- }
- validateCount++;
- return true;
- }
public void activate() {
if (destroyed) {
fail("attempted to activate a destroyed object");
}
activateCount++;
}
- public void passivate() {
- if (destroyed) {
- fail("attempted to passivate a destroyed object");
- }
- passivateCount++;
- }
- public void reset() {
- validateCount = 0;
- activateCount = 0;
- passivateCount = 0;
- destroyed = false;
- }
public void destroy() {
destroyed = true;
}
- public int getValidateCount() {
- return validateCount;
+ private void fail(final String message) {
+ throw new IllegalStateException(message);
}
public int getActivateCount() {
return activateCount;
}
+ public int getId() {
+ return id;
+ }
+ public K getKey() {
+ return key;
+ }
public int getPassivateCount() {
return passivateCount;
}
+ public int getValidateCount() {
+ return validateCount;
+ }
public boolean isDestroyed() {
return destroyed;
}
- public int getId() {
- return id;
+ public void passivate() {
+ if (destroyed) {
+ fail("attempted to passivate a destroyed object");
+ }
+ passivateCount++;
}
- public K getKey() {
- return key;
+ public void reset() {
+ validateCount = 0;
+ activateCount = 0;
+ passivateCount = 0;
+ destroyed = false;
}
@Override
public String toString() {
return "Key: " + key + " id: " + id;
}
- private void fail(final String message) {
- throw new IllegalStateException(message);
+ public boolean validate() {
+ if (destroyed) {
+ fail("attempted to validate a destroyed object");
+ }
+ validateCount++;
+ return true;
}
}
diff --git a/src/test/java/org/apache/commons/pool2/VisitTrackerFactory.java b/src/test/java/org/apache/commons/pool2/VisitTrackerFactory.java
index f00284d..7cb1f81 100644
--- a/src/test/java/org/apache/commons/pool2/VisitTrackerFactory.java
+++ b/src/test/java/org/apache/commons/pool2/VisitTrackerFactory.java
@@ -31,18 +31,13 @@ public class VisitTrackerFactory<K>
}
@Override
- public PooledObject<VisitTracker<K>> makeObject() {
- return new DefaultPooledObject<>(new VisitTracker<K>(nextId++));
- }
-
- @Override
- public PooledObject<VisitTracker<K>> makeObject(final K key) {
- return new DefaultPooledObject<>(new VisitTracker<>(nextId++, key));
+ public void activateObject(final K key, final PooledObject<VisitTracker<K>> ref) throws Exception {
+ ref.getObject().activate();
}
@Override
- public void destroyObject(final PooledObject<VisitTracker<K>> ref) {
- ref.getObject().destroy();
+ public void activateObject(final PooledObject<VisitTracker<K>> ref) throws Exception {
+ ref.getObject().activate();
}
@Override
@@ -51,23 +46,23 @@ public class VisitTrackerFactory<K>
}
@Override
- public boolean validateObject(final PooledObject<VisitTracker<K>> ref) {
- return ref.getObject().validate();
+ public void destroyObject(final PooledObject<VisitTracker<K>> ref) {
+ ref.getObject().destroy();
}
@Override
- public boolean validateObject(final K key, final PooledObject<VisitTracker<K>> ref) {
- return ref.getObject().validate();
+ public PooledObject<VisitTracker<K>> makeObject() {
+ return new DefaultPooledObject<>(new VisitTracker<K>(nextId++));
}
@Override
- public void activateObject(final PooledObject<VisitTracker<K>> ref) throws Exception {
- ref.getObject().activate();
+ public PooledObject<VisitTracker<K>> makeObject(final K key) {
+ return new DefaultPooledObject<>(new VisitTracker<>(nextId++, key));
}
@Override
- public void activateObject(final K key, final PooledObject<VisitTracker<K>> ref) throws Exception {
- ref.getObject().activate();
+ public void passivateObject(final K key, final PooledObject<VisitTracker<K>> ref) throws Exception {
+ ref.getObject().passivate();
}
@Override
@@ -75,12 +70,17 @@ public class VisitTrackerFactory<K>
ref.getObject().passivate();
}
+ public void resetId() {
+ nextId = 0;
+ }
+
@Override
- public void passivateObject(final K key, final PooledObject<VisitTracker<K>> ref) throws Exception {
- ref.getObject().passivate();
+ public boolean validateObject(final K key, final PooledObject<VisitTracker<K>> ref) {
+ return ref.getObject().validate();
}
- public void resetId() {
- nextId = 0;
+ @Override
+ public boolean validateObject(final PooledObject<VisitTracker<K>> ref) {
+ return ref.getObject().validate();
}
}
diff --git a/src/test/java/org/apache/commons/pool2/Waiter.java b/src/test/java/org/apache/commons/pool2/Waiter.java
index 377c144..52b9df1 100644
--- a/src/test/java/org/apache/commons/pool2/Waiter.java
+++ b/src/test/java/org/apache/commons/pool2/Waiter.java
@@ -55,6 +55,62 @@ public class Waiter {
}
}
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof Waiter)) {
+ return false;
+ }
+ return obj.hashCode() == id;
+ }
+
+ /**
+ * <p>Returns the last idle time for this instance in ms.</p>
+ *
+ * <p>When an instance is created, and each subsequent time it is passivated,
+ * the {@link #getLastPassivatedMillis() lastPassivated} property is updated with the
+ * current time. When the next activation occurs, {@code lastIdleTime} is
+ * updated with the elapsed time since passivation.<p>
+ *
+ * @return last idle time
+ */
+ public long getLastIdleTimeMillis() {
+ return lastIdleTimeMillis;
+ }
+
+ /**
+ * <p>Returns the system time of this instance's last passivation.</p>
+ *
+ * <p>When an instance is created, this field is initialized to the system time.</p>
+ *
+ * @return time of last passivation
+ */
+ public long getLastPassivatedMillis() {
+ return lastPassivatedMillis;
+ }
+
+ public long getLatency() {
+ return latency;
+ }
+
+ /**
+ * @return how many times this instance has been passivated
+ */
+ public long getPassivationCount() {
+ return passivationCount;
+ }
+
+ /**
+ * @return how many times this instance has been validated
+ */
+ public long getValidationCount() {
+ return validationCount;
+ }
+
+ @Override
+ public int hashCode() {
+ return id;
+ }
+
/**
* Whether or not the instance is active.
*
@@ -64,6 +120,11 @@ public class Waiter {
return active;
}
+ public boolean isValid() {
+ validationCount++;
+ return valid;
+ }
+
/**
* <p>Sets the active state and updates {@link #getLastIdleTimeMillis() lastIdleTime}
* or {@link #getLastPassivatedMillis() lastPassivated} as appropriate.</p>
@@ -95,75 +156,14 @@ public class Waiter {
}
}
- public long getLatency() {
- return latency;
- }
-
public void setLatency(final long latency) {
this.latency = latency;
}
- public boolean isValid() {
- validationCount++;
- return valid;
- }
-
public void setValid(final boolean valid) {
this.valid = valid;
}
- /**
- * <p>Returns the system time of this instance's last passivation.</p>
- *
- * <p>When an instance is created, this field is initialized to the system time.</p>
- *
- * @return time of last passivation
- */
- public long getLastPassivatedMillis() {
- return lastPassivatedMillis;
- }
-
- /**
- * <p>Returns the last idle time for this instance in ms.</p>
- *
- * <p>When an instance is created, and each subsequent time it is passivated,
- * the {@link #getLastPassivatedMillis() lastPassivated} property is updated with the
- * current time. When the next activation occurs, {@code lastIdleTime} is
- * updated with the elapsed time since passivation.<p>
- *
- * @return last idle time
- */
- public long getLastIdleTimeMillis() {
- return lastIdleTimeMillis;
- }
-
- /**
- * @return how many times this instance has been validated
- */
- public long getValidationCount() {
- return validationCount;
- }
-
- /**
- * @return how many times this instance has been passivated
- */
- public long getPassivationCount() {
- return passivationCount;
- }
-
- @Override
- public int hashCode() {
- return id;
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof Waiter)) {
- return false;
- }
- return obj.hashCode() == id;
- }
-
@Override
public String toString() {
final StringBuilder buff = new StringBuilder();
diff --git a/src/test/java/org/apache/commons/pool2/WaiterFactory.java b/src/test/java/org/apache/commons/pool2/WaiterFactory.java
index 1ab30f2..148ea22 100644
--- a/src/test/java/org/apache/commons/pool2/WaiterFactory.java
+++ b/src/test/java/org/apache/commons/pool2/WaiterFactory.java
@@ -69,6 +69,20 @@ KeyedPooledObjectFactory<K,Waiter> {
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;
@@ -82,18 +96,9 @@ KeyedPooledObjectFactory<K,Waiter> {
this.passivateInvalidationProbability = passivateInvalidationProbability;
}
- 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);
+ @Override
+ public void activateObject(final K key, final PooledObject<Waiter> obj) throws Exception {
+ activateObject(obj);
}
@Override
@@ -103,6 +108,15 @@ KeyedPooledObjectFactory<K,Waiter> {
}
@Override
+ public void destroyObject(final K key,final PooledObject<Waiter> obj) throws Exception {
+ 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) throws Exception {
doWait(destroyLatency);
obj.getObject().setValid(false);
@@ -113,35 +127,6 @@ KeyedPooledObjectFactory<K,Waiter> {
}
}
- @Override
- public PooledObject<Waiter> makeObject() throws Exception {
- // 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 void passivateObject(final PooledObject<Waiter> obj) throws Exception {
- obj.getObject().setActive(false);
- doWait(passivateLatency);
- if (Math.random() < passivateInvalidationProbability) {
- obj.getObject().setValid(false);
- }
- }
-
- @Override
- public boolean validateObject(final PooledObject<Waiter> obj) {
- doWait(validateLatency);
- return obj.getObject().isValid();
- }
-
protected void doWait(final long latency) {
if (latency == 0) {
return;
@@ -153,18 +138,6 @@ KeyedPooledObjectFactory<K,Waiter> {
}
}
- 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));
- }
- }
-
/**
* @return the maxActive
*/
@@ -172,20 +145,18 @@ KeyedPooledObjectFactory<K,Waiter> {
return maxActive;
}
- // KeyedPoolableObjectFactory methods
-
- @Override
- public void activateObject(final K key, final PooledObject<Waiter> obj) throws Exception {
- activateObject(obj);
- }
-
@Override
- public void destroyObject(final K key,final PooledObject<Waiter> obj) throws Exception {
- destroyObject(obj);
+ public PooledObject<Waiter> makeObject() throws Exception {
+ // Increment and test *before* make
synchronized (this) {
- final Integer count = activeCounts.get(key);
- activeCounts.put(key, Integer.valueOf(count.intValue() - 1));
+ 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
@@ -208,14 +179,43 @@ KeyedPooledObjectFactory<K,Waiter> {
return makeObject();
}
+ // KeyedPoolableObjectFactory methods
+
@Override
public void passivateObject(final K key, final PooledObject<Waiter> obj) throws Exception {
passivateObject(obj);
}
@Override
+ public void passivateObject(final PooledObject<Waiter> obj) throws Exception {
+ 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/CallStackTest.java b/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java
index f842fda..805ee3a 100644
--- a/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java
+++ b/src/test/java/org/apache/commons/pool2/impl/CallStackTest.java
@@ -30,8 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class CallStackTest {
- private final StringWriter writer = new StringWriter();
-
public static Stream<Arguments> data() {
return Stream.of(
Arguments.arguments( new ThrowableCallStack("Test", false)),
@@ -39,6 +37,8 @@ public class CallStackTest {
);
}
+ private final StringWriter writer = new StringWriter();
+
@ParameterizedTest
@MethodSource("data")
public void testPrintClearedStackTraceIsNoOp(CallStack stack) throws Exception {
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
index 2013ca4..0fccfcb 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestAbandonedObjectPool.java
@@ -42,12 +42,167 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+class PooledTestObject implements TrackedUse {
+ private static final AtomicInteger hash = new AtomicInteger();
+ private boolean active = false;
+ private boolean destroyed = false;
+ private int _hash = 0;
+ private boolean _abandoned = false;
+ private boolean detached = false; // destroy-abandoned "detaches"
+
+ public PooledTestObject() {
+ _hash = hash.incrementAndGet();
+ }
+
+ public void destroy(final DestroyMode mode) {
+ destroyed = true;
+ if (mode.equals(DestroyMode.ABANDONED)) {
+ detached = true;
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof PooledTestObject)) {
+ return false;
+ }
+ return obj.hashCode() == hashCode();
+ }
+
+ @Override
+ public long getLastUsed() {
+ if (_abandoned) {
+ // Abandoned object sweep will occur no matter what the value of removeAbandonedTimeout,
+ // because this indicates that this object was last used decades ago
+ return 1;
+ }
+ // Abandoned object sweep won't clean up this object
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return _hash;
+ }
+
+ public synchronized boolean isActive() {
+ return active;
+ }
+
+ public boolean isDestroyed() {
+ return destroyed;
+ }
+
+ public boolean isDetached() {
+ return detached;
+ }
+
+ public void setAbandoned(final boolean b) {
+ _abandoned = b;
+ }
+
+ public synchronized void setActive(final boolean b) {
+ active = b;
+ }
+}
+
/**
* TestCase for AbandonedObjectPool
*/
public class TestAbandonedObjectPool {
+ class ConcurrentBorrower extends Thread {
+ private final ArrayList<PooledTestObject> _borrowed;
+
+ public ConcurrentBorrower(final ArrayList<PooledTestObject> borrowed) {
+ _borrowed = borrowed;
+ }
+
+ @Override
+ public void run() {
+ try {
+ _borrowed.add(pool.borrowObject());
+ } catch (final Exception e) {
+ // expected in most cases
+ }
+ }
+ }
+ class ConcurrentReturner extends Thread {
+ private final PooledTestObject returned;
+ public ConcurrentReturner(final PooledTestObject obj) {
+ returned = obj;
+ }
+ @Override
+ public void run() {
+ try {
+ sleep(20);
+ pool.returnObject(returned);
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ private static class SimpleFactory implements PooledObjectFactory<PooledTestObject> {
+
+ private final long destroyLatency;
+ private final long validateLatency;
+
+ public SimpleFactory() {
+ destroyLatency = 0;
+ validateLatency = 0;
+ }
+
+ public SimpleFactory(final long destroyLatency, final long validateLatency) {
+ this.destroyLatency = destroyLatency;
+ this.validateLatency = validateLatency;
+ }
+
+ @Override
+ public void activateObject(final PooledObject<PooledTestObject> obj) {
+ obj.getObject().setActive(true);
+ }
+
+ @Override
+ public void destroyObject(final PooledObject<PooledTestObject> obj) throws Exception {
+ destroyObject(obj, DestroyMode.NORMAL);
+ }
+
+ @Override
+ public void destroyObject(final PooledObject<PooledTestObject> obj, final DestroyMode mode) throws Exception {
+ obj.getObject().setActive(false);
+ // while destroying instances, yield control to other threads
+ // helps simulate threading errors
+ Thread.yield();
+ if (destroyLatency != 0) {
+ Thread.sleep(destroyLatency);
+ }
+ obj.getObject().destroy(mode);
+ }
+
+ @Override
+ public PooledObject<PooledTestObject> makeObject() {
+ return new DefaultPooledObject<>(new PooledTestObject());
+ }
+
+ @Override
+ public void passivateObject(final PooledObject<PooledTestObject> obj) {
+ obj.getObject().setActive(false);
+ }
+
+ @Override
+ public boolean validateObject(final PooledObject<PooledTestObject> obj) {
+ try {
+ Thread.sleep(validateLatency);
+ } catch (final Exception ex) {
+ // ignore
+ }
+ return true;
+ }
+ }
+
private GenericObjectPool<PooledTestObject> pool = null;
+
private AbandonedConfig abandonedConfig = null;
@SuppressWarnings("deprecation")
@@ -98,51 +253,34 @@ public class TestAbandonedObjectPool {
}
/**
- * Tests fix for Bug 28579, a bug in AbandonedObjectPool that causes numActive to go negative
- * in GenericObjectPool
+ * Verify that an object that gets flagged as abandoned and is subsequently
+ * invalidated is only destroyed (and pool counter decremented) once.
*
* @throws Exception May occur in some failure modes
*/
@Test
- public void testConcurrentInvalidation() throws Exception {
- final int POOL_SIZE = 30;
- pool.setMaxTotal(POOL_SIZE);
- pool.setMaxIdle(POOL_SIZE);
+ public void testAbandonedInvalidate() throws Exception {
+ abandonedConfig = new AbandonedConfig();
+ abandonedConfig.setRemoveAbandonedOnMaintenance(true);
+ abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND);
+ pool.close(); // Unregister pool created by setup
+ pool = new GenericObjectPool<>(
+ // destroys take 200 ms
+ new SimpleFactory(200, 0),
+ new GenericObjectPoolConfig<PooledTestObject>(), abandonedConfig);
+ final int n = 10;
+ pool.setMaxTotal(n);
pool.setBlockWhenExhausted(false);
-
- // Exhaust the connection pool
- final ArrayList<PooledTestObject> vec = new ArrayList<>();
- for (int i = 0; i < POOL_SIZE; i++) {
- vec.add(pool.borrowObject());
- }
-
- // Abandon all borrowed objects
- for (final PooledTestObject element : vec) {
- element.setAbandoned(true);
- }
-
- // Try launching a bunch of borrows concurrently. Abandoned sweep will be triggered for each.
- final int CONCURRENT_BORROWS = 5;
- final Thread[] threads = new Thread[CONCURRENT_BORROWS];
- for (int i = 0; i < CONCURRENT_BORROWS; i++) {
- threads[i] = new ConcurrentBorrower(vec);
- threads[i].start();
- }
-
- // Wait for all the threads to finish
- for (int i = 0; i < CONCURRENT_BORROWS; i++) {
- threads[i].join();
- }
-
- // Return all objects that have not been destroyed
- for (final PooledTestObject pto : vec) {
- if (pto.isActive()) {
- pool.returnObject(pto);
- }
+ pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+ PooledTestObject obj = null;
+ for (int i = 0; i < 5; i++) {
+ obj = pool.borrowObject();
}
-
- // Now, the number of active instances should be 0
- assertTrue( pool.getNumActive() == 0,"numActive should have been 0, was " + pool.getNumActive());
+ Thread.sleep(1000); // abandon checked out instances and let evictor start
+ pool.invalidateObject(obj); // Should not trigger another destroy / decrement
+ Thread.sleep(2000); // give evictor time to finish destroys
+ assertEquals(0, pool.getNumActive());
+ assertEquals(5, pool.getDestroyedCount());
}
/**
@@ -182,34 +320,51 @@ public class TestAbandonedObjectPool {
}
/**
- * Verify that an object that gets flagged as abandoned and is subsequently
- * invalidated is only destroyed (and pool counter decremented) once.
+ * Tests fix for Bug 28579, a bug in AbandonedObjectPool that causes numActive to go negative
+ * in GenericObjectPool
*
* @throws Exception May occur in some failure modes
*/
@Test
- public void testAbandonedInvalidate() throws Exception {
- abandonedConfig = new AbandonedConfig();
- abandonedConfig.setRemoveAbandonedOnMaintenance(true);
- abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND);
- pool.close(); // Unregister pool created by setup
- pool = new GenericObjectPool<>(
- // destroys take 200 ms
- new SimpleFactory(200, 0),
- new GenericObjectPoolConfig<PooledTestObject>(), abandonedConfig);
- final int n = 10;
- pool.setMaxTotal(n);
+ public void testConcurrentInvalidation() throws Exception {
+ final int POOL_SIZE = 30;
+ pool.setMaxTotal(POOL_SIZE);
+ pool.setMaxIdle(POOL_SIZE);
pool.setBlockWhenExhausted(false);
- pool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
- PooledTestObject obj = null;
- for (int i = 0; i < 5; i++) {
- obj = pool.borrowObject();
+
+ // Exhaust the connection pool
+ final ArrayList<PooledTestObject> vec = new ArrayList<>();
+ for (int i = 0; i < POOL_SIZE; i++) {
+ vec.add(pool.borrowObject());
}
- Thread.sleep(1000); // abandon checked out instances and let evictor start
- pool.invalidateObject(obj); // Should not trigger another destroy / decrement
- Thread.sleep(2000); // give evictor time to finish destroys
- assertEquals(0, pool.getNumActive());
- assertEquals(5, pool.getDestroyedCount());
+
+ // Abandon all borrowed objects
+ for (final PooledTestObject element : vec) {
+ element.setAbandoned(true);
+ }
+
+ // Try launching a bunch of borrows concurrently. Abandoned sweep will be triggered for each.
+ final int CONCURRENT_BORROWS = 5;
+ final Thread[] threads = new Thread[CONCURRENT_BORROWS];
+ for (int i = 0; i < CONCURRENT_BORROWS; i++) {
+ threads[i] = new ConcurrentBorrower(vec);
+ threads[i].start();
+ }
+
+ // Wait for all the threads to finish
+ for (int i = 0; i < CONCURRENT_BORROWS; i++) {
+ threads[i].join();
+ }
+
+ // Return all objects that have not been destroyed
+ for (final PooledTestObject pto : vec) {
+ if (pto.isActive()) {
+ pool.returnObject(pto);
+ }
+ }
+
+ // Now, the number of active instances should be 0
+ assertTrue( pool.getNumActive() == 0,"numActive should have been 0, was " + pool.getNumActive());
}
public void testDestroyModeAbandoned() throws Exception {
@@ -272,6 +427,27 @@ public class TestAbandonedObjectPool {
}
/**
+ * JIRA: POOL-300
+ */
+ @Test
+ public void testStackTrace() throws Exception {
+ abandonedConfig.setRemoveAbandonedOnMaintenance(true);
+ abandonedConfig.setLogAbandoned(true);
+ abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND);
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final BufferedOutputStream bos = new BufferedOutputStream(baos);
+ final PrintWriter pw = new PrintWriter(bos);
+ abandonedConfig.setLogWriter(pw);
+ pool.setAbandonedConfig(abandonedConfig);
+ pool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ final PooledTestObject o1 = pool.borrowObject();
+ Thread.sleep(2000);
+ assertTrue(o1.isDestroyed());
+ bos.flush();
+ assertTrue(baos.toString().indexOf("Pooled object") >= 0);
+ }
+
+ /**
* Test case for https://issues.apache.org/jira/browse/DBCP-260.
* Borrow and abandon all the available objects then attempt to borrow one
* further object which should block until the abandoned objects are
@@ -299,181 +475,5 @@ public class TestAbandonedObjectPool {
assertTrue(endMillis - startMillis < 5000);
}
-
- /**
- * JIRA: POOL-300
- */
- @Test
- public void testStackTrace() throws Exception {
- abandonedConfig.setRemoveAbandonedOnMaintenance(true);
- abandonedConfig.setLogAbandoned(true);
- abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND);
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- final BufferedOutputStream bos = new BufferedOutputStream(baos);
- final PrintWriter pw = new PrintWriter(bos);
- abandonedConfig.setLogWriter(pw);
- pool.setAbandonedConfig(abandonedConfig);
- pool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- final PooledTestObject o1 = pool.borrowObject();
- Thread.sleep(2000);
- assertTrue(o1.isDestroyed());
- bos.flush();
- assertTrue(baos.toString().indexOf("Pooled object") >= 0);
- }
-
- class ConcurrentBorrower extends Thread {
- private final ArrayList<PooledTestObject> _borrowed;
-
- public ConcurrentBorrower(final ArrayList<PooledTestObject> borrowed) {
- _borrowed = borrowed;
- }
-
- @Override
- public void run() {
- try {
- _borrowed.add(pool.borrowObject());
- } catch (final Exception e) {
- // expected in most cases
- }
- }
- }
-
- class ConcurrentReturner extends Thread {
- private final PooledTestObject returned;
- public ConcurrentReturner(final PooledTestObject obj) {
- returned = obj;
- }
- @Override
- public void run() {
- try {
- sleep(20);
- pool.returnObject(returned);
- } catch (final Exception e) {
- // ignore
- }
- }
- }
-
- private static class SimpleFactory implements PooledObjectFactory<PooledTestObject> {
-
- private final long destroyLatency;
- private final long validateLatency;
-
- public SimpleFactory() {
- destroyLatency = 0;
- validateLatency = 0;
- }
-
- public SimpleFactory(final long destroyLatency, final long validateLatency) {
- this.destroyLatency = destroyLatency;
- this.validateLatency = validateLatency;
- }
-
- @Override
- public PooledObject<PooledTestObject> makeObject() {
- return new DefaultPooledObject<>(new PooledTestObject());
- }
-
- @Override
- public boolean validateObject(final PooledObject<PooledTestObject> obj) {
- try {
- Thread.sleep(validateLatency);
- } catch (final Exception ex) {
- // ignore
- }
- return true;
- }
-
- @Override
- public void activateObject(final PooledObject<PooledTestObject> obj) {
- obj.getObject().setActive(true);
- }
-
- @Override
- public void passivateObject(final PooledObject<PooledTestObject> obj) {
- obj.getObject().setActive(false);
- }
-
- @Override
- public void destroyObject(final PooledObject<PooledTestObject> obj) throws Exception {
- destroyObject(obj, DestroyMode.NORMAL);
- }
-
- @Override
- public void destroyObject(final PooledObject<PooledTestObject> obj, final DestroyMode mode) throws Exception {
- obj.getObject().setActive(false);
- // while destroying instances, yield control to other threads
- // helps simulate threading errors
- Thread.yield();
- if (destroyLatency != 0) {
- Thread.sleep(destroyLatency);
- }
- obj.getObject().destroy(mode);
- }
- }
-}
-
-class PooledTestObject implements TrackedUse {
- private boolean active = false;
- private boolean destroyed = false;
- private int _hash = 0;
- private boolean _abandoned = false;
- private static final AtomicInteger hash = new AtomicInteger();
- private boolean detached = false; // destroy-abandoned "detaches"
-
- public PooledTestObject() {
- _hash = hash.incrementAndGet();
- }
-
- public synchronized void setActive(final boolean b) {
- active = b;
- }
-
- public synchronized boolean isActive() {
- return active;
- }
-
- public void destroy(final DestroyMode mode) {
- destroyed = true;
- if (mode.equals(DestroyMode.ABANDONED)) {
- detached = true;
- }
- }
-
- public boolean isDestroyed() {
- return destroyed;
- }
-
- public boolean isDetached() {
- return detached;
- }
-
- @Override
- public int hashCode() {
- return _hash;
- }
-
- public void setAbandoned(final boolean b) {
- _abandoned = b;
- }
-
- @Override
- public long getLastUsed() {
- if (_abandoned) {
- // Abandoned object sweep will occur no matter what the value of removeAbandonedTimeout,
- // because this indicates that this object was last used decades ago
- return 1;
- }
- // Abandoned object sweep won't clean up this object
- return 0;
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof PooledTestObject)) {
- return false;
- }
- return obj.hashCode() == hashCode();
- }
}
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
index 2007e0c..30307d2 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
@@ -48,6 +48,14 @@ public class TestBaseGenericObjectPool {
}
@Test
+ public void testActiveTimeStatistics() {
+ for (int i = 0; i < 99; i++) { // must be < MEAN_TIMING_STATS_CACHE_SIZE
+ pool.updateStatsReturn(i);
+ }
+ assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE);
+ }
+
+ @Test
public void testBorrowWaitStatistics() {
final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject();
pool.updateStatsBorrow(p, 10);
@@ -72,14 +80,6 @@ public class TestBaseGenericObjectPool {
}
@Test
- public void testActiveTimeStatistics() {
- for (int i = 0; i < 99; i++) { // must be < MEAN_TIMING_STATS_CACHE_SIZE
- pool.updateStatsReturn(i);
- }
- assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE);
- }
-
- @Test
public void testEvictionTimerMultiplePools() throws InterruptedException {
final AtomicIntegerFactory factory = new AtomicIntegerFactory();
factory.setValidateLatency(50);
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java
index 50b3050..333f594 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObjectInfo.java
@@ -28,28 +28,31 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestDefaultPooledObjectInfo {
@Test
- public void testTiming() throws Exception {
- try (final GenericObjectPool<String> pool = new GenericObjectPool<>(new SimpleFactory())) {
-
- final long t1Millis = System.currentTimeMillis();
+ public void testGetLastBorrowTrace() throws Exception {
+ final AbandonedConfig abandonedConfig = new AbandonedConfig();
- Thread.sleep(50);
- final String s1 = pool.borrowObject();
- Thread.sleep(50);
+ abandonedConfig.setRemoveAbandonedOnBorrow(true);
+ abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND);
+ abandonedConfig.setLogAbandoned(true);
+ try (final GenericObjectPool<String> pool = new GenericObjectPool<>(new SimpleFactory(),
+ new GenericObjectPoolConfig<String>(), abandonedConfig)) {
- final long t2Millis = System.currentTimeMillis();
+ pool.borrowObject();
+ // pool.returnObject(s1); // Object not returned, causes abandoned object created exception
- Thread.sleep(50);
- pool.returnObject(s1);
- Thread.sleep(50);
+ final Set<DefaultPooledObjectInfo> strings = pool.listAllObjects();
+ final DefaultPooledObjectInfo s1Info = strings.iterator().next();
+ final String lastBorrowTrace = s1Info.getLastBorrowTrace();
- final long t3Millis = System.currentTimeMillis();
+ assertTrue(lastBorrowTrace.startsWith("Pooled object created"));
+ }
+ }
- Thread.sleep(50);
- pool.borrowObject();
- Thread.sleep(50);
+ @Test
+ public void testGetPooledObjectToString() throws Exception {
+ try (final GenericObjectPool<String> pool = new GenericObjectPool<>(new SimpleFactory())) {
- final long t4Millis = System.currentTimeMillis();
+ final String s1 = pool.borrowObject();
final Set<DefaultPooledObjectInfo> strings = pool.listAllObjects();
@@ -57,21 +60,7 @@ public class TestDefaultPooledObjectInfo {
final DefaultPooledObjectInfo s1Info = strings.iterator().next();
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
-
- assertTrue(s1Info.getCreateTime() > t1Millis);
- assertEquals(sdf.format(Long.valueOf(s1Info.getCreateTime())), s1Info.getCreateTimeFormatted());
- assertTrue(s1Info.getCreateTime() < t2Millis);
-
- assertTrue(s1Info.getLastReturnTime() > t2Millis);
- assertEquals(sdf.format(Long.valueOf(s1Info.getLastReturnTime())),
- s1Info.getLastReturnTimeFormatted());
- assertTrue(s1Info.getLastReturnTime() < t3Millis);
-
- assertTrue(s1Info.getLastBorrowTime() > t3Millis);
- assertEquals(sdf.format(Long.valueOf(s1Info.getLastBorrowTime())),
- s1Info.getLastBorrowTimeFormatted());
- assertTrue(s1Info.getLastBorrowTime() < t4Millis);
+ assertEquals(s1, s1Info.getPooledObjectToString());
}
}
@@ -92,39 +81,50 @@ public class TestDefaultPooledObjectInfo {
}
@Test
- public void testGetPooledObjectToString() throws Exception {
+ public void testTiming() throws Exception {
try (final GenericObjectPool<String> pool = new GenericObjectPool<>(new SimpleFactory())) {
+ final long t1Millis = System.currentTimeMillis();
+
+ Thread.sleep(50);
final String s1 = pool.borrowObject();
+ Thread.sleep(50);
- final Set<DefaultPooledObjectInfo> strings = pool.listAllObjects();
+ final long t2Millis = System.currentTimeMillis();
- assertEquals(1, strings.size());
+ Thread.sleep(50);
+ pool.returnObject(s1);
+ Thread.sleep(50);
- final DefaultPooledObjectInfo s1Info = strings.iterator().next();
+ final long t3Millis = System.currentTimeMillis();
- assertEquals(s1, s1Info.getPooledObjectToString());
- }
- }
+ Thread.sleep(50);
+ pool.borrowObject();
+ Thread.sleep(50);
- @Test
- public void testGetLastBorrowTrace() throws Exception {
- final AbandonedConfig abandonedConfig = new AbandonedConfig();
+ final long t4Millis = System.currentTimeMillis();
- abandonedConfig.setRemoveAbandonedOnBorrow(true);
- abandonedConfig.setRemoveAbandonedTimeout(TestConstants.ONE_SECOND);
- abandonedConfig.setLogAbandoned(true);
- try (final GenericObjectPool<String> pool = new GenericObjectPool<>(new SimpleFactory(),
- new GenericObjectPoolConfig<String>(), abandonedConfig)) {
+ final Set<DefaultPooledObjectInfo> strings = pool.listAllObjects();
- pool.borrowObject();
- // pool.returnObject(s1); // Object not returned, causes abandoned object created exception
+ assertEquals(1, strings.size());
- final Set<DefaultPooledObjectInfo> strings = pool.listAllObjects();
final DefaultPooledObjectInfo s1Info = strings.iterator().next();
- final String lastBorrowTrace = s1Info.getLastBorrowTrace();
- assertTrue(lastBorrowTrace.startsWith("Pooled object created"));
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
+
+ assertTrue(s1Info.getCreateTime() > t1Millis);
+ assertEquals(sdf.format(Long.valueOf(s1Info.getCreateTime())), s1Info.getCreateTimeFormatted());
+ assertTrue(s1Info.getCreateTime() < t2Millis);
+
+ assertTrue(s1Info.getLastReturnTime() > t2Millis);
+ assertEquals(sdf.format(Long.valueOf(s1Info.getLastReturnTime())),
+ s1Info.getLastReturnTimeFormatted());
+ assertTrue(s1Info.getLastReturnTime() < t3Millis);
+
+ assertTrue(s1Info.getLastBorrowTime() > t3Millis);
+ assertEquals(sdf.format(Long.valueOf(s1Info.getLastBorrowTime())),
+ s1Info.getLastBorrowTimeFormatted());
+ assertTrue(s1Info.getLastBorrowTime() < t4Millis);
}
}
}
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 74aed0e..f32e053 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
@@ -69,1239 +69,1443 @@ import org.junit.jupiter.api.Timeout;
*/
public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
- @Override
- protected KeyedObjectPool<Object,Object> makeEmptyPool(final int minCapacity) {
- final KeyedPooledObjectFactory<Object,Object> perKeyFactory =
- new SimplePerKeyFactory();
- final GenericKeyedObjectPool<Object,Object> perKeyPool =
- new GenericKeyedObjectPool<>(perKeyFactory);
- perKeyPool.setMaxTotalPerKey(minCapacity);
- perKeyPool.setMaxIdlePerKey(minCapacity);
- return perKeyPool;
- }
-
- @Override
- protected KeyedObjectPool<Object,Object> makeEmptyPool(final KeyedPooledObjectFactory<Object,Object> fac) {
- return new GenericKeyedObjectPool<>(fac);
+ private static class DaemonThreadFactory implements ThreadFactory {
+ @Override
+ public Thread newThread(final Runnable r) {
+ final Thread t = new Thread(r);
+ t.setDaemon(true);
+ return t;
+ }
}
- @Override
- protected Object getNthObject(final Object key, final int n) {
- return String.valueOf(key) + String.valueOf(n);
+ private static class DummyFactory
+ extends BaseKeyedPooledObjectFactory<Object,Object> {
+ @Override
+ public Object create(final Object key) throws Exception {
+ return null;
+ }
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
+ }
}
- @Override
- protected Object makeKey(final int n) {
- return String.valueOf(n);
+ /**
+ * 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 BaseKeyedPooledObjectFactory<String, HashSet<String>> {
+ @Override
+ public HashSet<String> create(final String key) throws Exception {
+ return new HashSet<>();
+ }
+ @Override
+ public PooledObject<HashSet<String>> wrap(final HashSet<String> value) {
+ return new DefaultPooledObject<>(value);
+ }
}
- /** setUp(): {@code new GenericKeyedObjectPool<String,String>(factory)} */
- private GenericKeyedObjectPool<String,String> gkoPool = null;
- /** setUp(): {@code new SimpleFactory<String>()} */
- private SimpleFactory<String> simpleFactory = null;
- private static final Integer KEY_ZERO = Integer.valueOf(0);
- private static final Integer KEY_ONE = Integer.valueOf(1);
- private static final Integer KEY_TWO = Integer.valueOf(2);
-
- @BeforeEach
- public void setUp() throws Exception {
- simpleFactory = new SimpleFactory<>();
- gkoPool = new GenericKeyedObjectPool<>(simpleFactory);
+ /**
+ * Attempts to invalidate an object, swallowing IllegalStateException.
+ */
+ static class InvalidateThread implements Runnable {
+ private final String obj;
+ private final KeyedObjectPool<String, String> pool;
+ private final String key;
+ private boolean done = false;
+ public InvalidateThread(final KeyedObjectPool<String, String> pool, final String key, final String obj) {
+ this.obj = obj;
+ this.pool = pool;
+ this.key = key;
+ }
+ public boolean complete() {
+ return done;
+ }
+ @Override
+ public void run() {
+ try {
+ pool.invalidateObject(key, obj);
+ } catch (final IllegalStateException ex) {
+ // Ignore
+ } catch (final Exception ex) {
+ fail("Unexpected exception " + ex.toString());
+ } finally {
+ done = true;
+ }
+ }
}
- @Override
- @AfterEach
- public void tearDown() throws Exception {
- super.tearDown();
- final ObjectName jmxName = gkoPool.getJmxName();
- final String poolName = Objects.toString(jmxName, null);
- gkoPool.clear();
- gkoPool.close();
- gkoPool = null;
- simpleFactory = null;
+ private static class ObjectFactory
+ extends BaseKeyedPooledObjectFactory<Integer, Object> {
- final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- final Set<ObjectName> result = mbs.queryNames(new ObjectName(
- "org.apache.commoms.pool2:type=GenericKeyedObjectPool,*"),
- 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);
+ @Override
+ public Object create(final Integer key)
+ throws Exception {
+ return new Object();
}
- assertEquals( 0, registeredPoolCount, msg.toString());
- }
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testNegativeMaxTotalPerKey() throws Exception {
- gkoPool.setMaxTotalPerKey(-1);
- gkoPool.setBlockWhenExhausted(false);
- final String obj = gkoPool.borrowObject("");
- assertEquals("0",obj);
- gkoPool.returnObject("",obj);
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
+ }
}
+ public static class SimpleFactory<K> implements KeyedPooledObjectFactory<K,String> {
+ volatile int counter = 0;
+ final boolean valid;
+ int activeCount = 0;
+ int validateCounter = 0;
+ boolean evenValid = true;
+ boolean oddValid = true;
+ boolean enableValidation = false;
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testNumActiveNumIdle2() throws Exception {
- assertEquals(0,gkoPool.getNumActive());
- assertEquals(0,gkoPool.getNumIdle());
- assertEquals(0,gkoPool.getNumActive("A"));
- assertEquals(0,gkoPool.getNumIdle("A"));
- assertEquals(0,gkoPool.getNumActive("B"));
- assertEquals(0,gkoPool.getNumIdle("B"));
-
- final String objA0 = gkoPool.borrowObject("A");
- final String objB0 = gkoPool.borrowObject("B");
-
- assertEquals(2,gkoPool.getNumActive());
- assertEquals(0,gkoPool.getNumIdle());
- assertEquals(1,gkoPool.getNumActive("A"));
- assertEquals(0,gkoPool.getNumIdle("A"));
- assertEquals(1,gkoPool.getNumActive("B"));
- assertEquals(0,gkoPool.getNumIdle("B"));
-
- final String objA1 = gkoPool.borrowObject("A");
- final String objB1 = gkoPool.borrowObject("B");
-
- assertEquals(4,gkoPool.getNumActive());
- assertEquals(0,gkoPool.getNumIdle());
- assertEquals(2,gkoPool.getNumActive("A"));
- assertEquals(0,gkoPool.getNumIdle("A"));
- assertEquals(2,gkoPool.getNumActive("B"));
- assertEquals(0,gkoPool.getNumIdle("B"));
-
- gkoPool.returnObject("A",objA0);
- gkoPool.returnObject("B",objB0);
-
- assertEquals(2,gkoPool.getNumActive());
- assertEquals(2,gkoPool.getNumIdle());
- assertEquals(1,gkoPool.getNumActive("A"));
- assertEquals(1,gkoPool.getNumIdle("A"));
- assertEquals(1,gkoPool.getNumActive("B"));
- assertEquals(1,gkoPool.getNumIdle("B"));
-
- gkoPool.returnObject("A",objA1);
- gkoPool.returnObject("B",objB1);
+ long destroyLatency = 0;
+ long makeLatency = 0;
+ long validateLatency = 0;
+ volatile int maxTotalPerKey = Integer.MAX_VALUE;
+ boolean exceptionOnPassivate = false;
+ boolean exceptionOnActivate = false;
+ boolean exceptionOnDestroy = false;
- assertEquals(0,gkoPool.getNumActive());
- assertEquals(4,gkoPool.getNumIdle());
- assertEquals(0,gkoPool.getNumActive("A"));
- assertEquals(2,gkoPool.getNumIdle("A"));
- assertEquals(0,gkoPool.getNumActive("B"));
- assertEquals(2,gkoPool.getNumIdle("B"));
- }
+ boolean exceptionOnCreate = false;
- @Test
- public void testReturnObjectThrowsIllegalStateException() {
- try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(new SimpleFactory<String>())) {
- assertThrows(IllegalStateException.class,
- () -> pool.returnObject("Foo", "Bar"));
+ public SimpleFactory() {
+ this(true);
}
- }
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxIdle() throws Exception {
- gkoPool.setMaxTotalPerKey(100);
- gkoPool.setMaxIdlePerKey(8);
- final String[] active = new String[100];
- for(int i=0;i<100;i++) {
- active[i] = gkoPool.borrowObject("");
- }
- assertEquals(100,gkoPool.getNumActive(""));
- assertEquals(0,gkoPool.getNumIdle(""));
- for(int i=0;i<100;i++) {
- gkoPool.returnObject("",active[i]);
- assertEquals(99 - i,gkoPool.getNumActive(""));
- assertEquals((i < 8 ? i+1 : 8),gkoPool.getNumIdle(""));
+ public SimpleFactory(final boolean valid) {
+ this.valid = valid;
}
- for(int i=0;i<100;i++) {
- active[i] = gkoPool.borrowObject("a");
+ @Override
+ public void activateObject(final K key, final PooledObject<String> obj) throws Exception {
+ if (exceptionOnActivate) {
+ if (!(validateCounter++%2 == 0 ? evenValid : oddValid)) {
+ throw new Exception();
+ }
+ }
}
- assertEquals(100,gkoPool.getNumActive("a"));
- assertEquals(0,gkoPool.getNumIdle("a"));
- 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"));
+ @Override
+ public void destroyObject(final K key, final PooledObject<String> obj) throws Exception {
+ doWait(destroyLatency);
+ synchronized(this) {
+ activeCount--;
+ }
+ if (exceptionOnDestroy) {
+ throw new Exception();
+ }
}
- // total number of idle instances is twice maxIdle
- assertEquals(16, gkoPool.getNumIdle());
- // Each pool is at the sup
- assertEquals(8, gkoPool.getNumIdle(""));
- assertEquals(8, gkoPool.getNumIdle("a"));
+ private void doWait(final long latency) {
+ try {
+ Thread.sleep(latency);
+ } catch (final InterruptedException ex) {
+ // ignore
+ }
+ }
+ @Override
+ public PooledObject<String> makeObject(final K key) throws Exception {
+ if (exceptionOnCreate) {
+ throw new Exception();
+ }
+ doWait(makeLatency);
+ String out = null;
+ synchronized(this) {
+ activeCount++;
+ if (activeCount > maxTotalPerKey) {
+ throw new IllegalStateException(
+ "Too many active instances: " + activeCount);
+ }
+ out = String.valueOf(key) + String.valueOf(counter++);
+ }
+ return new DefaultPooledObject<>(out);
+ }
+ @Override
+ public void passivateObject(final K key, final PooledObject<String> obj) throws Exception {
+ if (exceptionOnPassivate) {
+ throw new Exception();
+ }
+ }
+ 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;
+ }
+ 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;
+ }
+ @Override
+ public boolean validateObject(final K key, final PooledObject<String> obj) {
+ doWait(validateLatency);
+ if (enableValidation) {
+ return validateCounter++%2 == 0 ? evenValid : oddValid;
+ }
+ return valid;
+ }
}
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalPerKey() throws Exception {
- gkoPool.setMaxTotalPerKey(3);
- gkoPool.setBlockWhenExhausted(false);
-
- gkoPool.borrowObject("");
- gkoPool.borrowObject("");
- gkoPool.borrowObject("");
- try {
- gkoPool.borrowObject("");
- fail("Expected NoSuchElementException");
- } catch(final NoSuchElementException e) {
- // expected
+ private static class SimplePerKeyFactory
+ extends BaseKeyedPooledObjectFactory<Object,Object> {
+ final ConcurrentHashMap<Object,AtomicInteger> map =
+ new ConcurrentHashMap<>();
+ @Override
+ public Object create(final Object key) throws Exception {
+ int counter = 0;
+ final AtomicInteger Counter = map.get(key);
+ if(null != Counter) {
+ counter = Counter.incrementAndGet();
+ } else {
+ map.put(key, new AtomicInteger(0));
+ counter = 0;
+ }
+ return String.valueOf(key) + String.valueOf(counter);
+ }
+ @Override
+ public PooledObject<Object> wrap(final Object value) {
+ return new DefaultPooledObject<>(value);
}
}
+ /*
+ * Very simple test thread that just tries to borrow an object from
+ * the provided pool with the specified key and returns it
+ */
+ static class SimpleTestThread<T> implements Runnable {
+ private final KeyedObjectPool<String,T> _pool;
+ private final String _key;
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalPerKeyZero() throws Exception {
- gkoPool.setMaxTotalPerKey(0);
- gkoPool.setBlockWhenExhausted(false);
+ public SimpleTestThread(final KeyedObjectPool<String,T> pool, final String key) {
+ _pool = pool;
+ _key = key;
+ }
- try {
- gkoPool.borrowObject("a");
- fail("Expected NoSuchElementException");
- } catch(final NoSuchElementException e) {
- // expected
+ @Override
+ public void run() {
+ try {
+ final T obj = _pool.borrowObject(_key);
+ _pool.returnObject(_key, obj);
+ } catch (final Exception e) {
+ // Ignore
+ }
}
}
+ /*
+ * DefaultEvictionPolicy modified to add latency
+ */
+ private static class SlowEvictionPolicy<T> extends DefaultEvictionPolicy<T> {
+ private final long delay;
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotal() throws Exception {
- gkoPool.setMaxTotalPerKey(2);
- gkoPool.setMaxTotal(3);
- gkoPool.setBlockWhenExhausted(false);
-
- final String o1 = gkoPool.borrowObject("a");
- assertNotNull(o1);
- final String o2 = gkoPool.borrowObject("a");
- assertNotNull(o2);
- final String o3 = gkoPool.borrowObject("b");
- assertNotNull(o3);
- try {
- gkoPool.borrowObject("c");
- fail("Expected NoSuchElementException");
- } catch(final NoSuchElementException e) {
- // expected
+ /**
+ * Create SlowEvictionPolicy with the given delay in ms
+ *
+ * @param delay number of ms of latency to inject in evict
+ */
+ public SlowEvictionPolicy(final long delay) {
+ this.delay = delay;
}
- assertEquals(0, gkoPool.getNumIdle());
+ @Override
+ public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
+ final int idleCount) {
+ try {
+ Thread.sleep(delay);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ return super.evict(config, underTest, idleCount);
+ }
+ }
- gkoPool.returnObject("b", o3);
- assertEquals(1, gkoPool.getNumIdle());
- assertEquals(1, gkoPool.getNumIdle("b"));
+ static class TestThread<T> implements Runnable {
+ private final java.util.Random _random = new java.util.Random();
- final Object o4 = gkoPool.borrowObject("b");
- assertNotNull(o4);
- assertEquals(0, gkoPool.getNumIdle());
- assertEquals(0, gkoPool.getNumIdle("b"));
+ /** GKOP to hit */
+ private final KeyedObjectPool<String,T> _pool;
+ /** number of borrow/return iterations */
+ private final int _iter;
+ /** delay before borrow */
+ private final int _startDelay;
+ /** delay before return */
+ private final int _holdTime;
+ /** whether or not delays are random (with max = configured values) */
+ private final boolean _randomDelay;
+ /** expected object */
+ private final T _expectedObject;
+ /** key used in borrow / return sequence - null means random */
+ private final String _key;
- gkoPool.setMaxTotal(4);
- final Object o5 = gkoPool.borrowObject("b");
- assertNotNull(o5);
+ private volatile boolean _complete = false;
+ private volatile boolean _failed = false;
+ private volatile Exception _exception;
- assertEquals(2, gkoPool.getNumActive("a"));
- assertEquals(2, gkoPool.getNumActive("b"));
- assertEquals(gkoPool.getMaxTotal(),
- gkoPool.getNumActive("b") + gkoPool.getNumActive("b"));
- assertEquals(gkoPool.getNumActive(),
- gkoPool.getMaxTotal());
- }
+ public TestThread(final KeyedObjectPool<String,T> pool) {
+ this(pool, 100, 50, 50, true, null, null);
+ }
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalZero() throws Exception {
- gkoPool.setMaxTotal(0);
- gkoPool.setBlockWhenExhausted(false);
+ public TestThread(final KeyedObjectPool<String,T> pool, final int iter) {
+ this(pool, iter, 50, 50, true, null, null);
+ }
- try {
- gkoPool.borrowObject("a");
- fail("Expected NoSuchElementException");
- } catch(final NoSuchElementException e) {
- // expected
+ public TestThread(final KeyedObjectPool<String,T> pool, final int iter, final int delay) {
+ this(pool, iter, delay, delay, true, null, null);
}
- }
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalLRU() throws Exception {
- gkoPool.setMaxTotalPerKey(2);
- gkoPool.setMaxTotal(3);
+ public TestThread(final KeyedObjectPool<String,T> pool, final int iter, final int startDelay,
+ final int holdTime, final boolean randomDelay, final T expectedObject, final String key) {
+ _pool = pool;
+ _iter = iter;
+ _startDelay = startDelay;
+ _holdTime = holdTime;
+ _randomDelay = randomDelay;
+ _expectedObject = expectedObject;
+ _key = key;
- final String o1 = gkoPool.borrowObject("a");
- assertNotNull(o1);
- gkoPool.returnObject("a", o1);
- Thread.sleep(25);
+ }
- final String o2 = gkoPool.borrowObject("b");
- assertNotNull(o2);
- gkoPool.returnObject("b", o2);
- Thread.sleep(25);
+ public boolean complete() {
+ return _complete;
+ }
- final String o3 = gkoPool.borrowObject("c");
- assertNotNull(o3);
- gkoPool.returnObject("c", o3);
- Thread.sleep(25);
-
- final String o4 = gkoPool.borrowObject("a");
- assertNotNull(o4);
- gkoPool.returnObject("a", o4);
- Thread.sleep(25);
-
- assertSame(o1, o4);
+ public boolean failed() {
+ return _failed;
+ }
- // this should cause b to be bumped out of the pool
- final String o5 = gkoPool.borrowObject("d");
- assertNotNull(o5);
- gkoPool.returnObject("d", o5);
- Thread.sleep(25);
+ @Override
+ public void run() {
+ for(int i=0;i<_iter;i++) {
+ final String key = _key == null ? String.valueOf(_random.nextInt(3)) : _key;
+ try {
+ Thread.sleep(_randomDelay ? _random.nextInt(_startDelay) : _startDelay);
+ } catch(final InterruptedException e) {
+ // ignored
+ }
+ T obj = null;
+ try {
+ obj = _pool.borrowObject(key);
+ } catch(final Exception e) {
+ _exception = e;
+ _failed = true;
+ _complete = true;
+ break;
+ }
- // now re-request b, we should get a different object because it should
- // have been expelled from pool (was oldest because a was requested after b)
- final String o6 = gkoPool.borrowObject("b");
- assertNotNull(o6);
- gkoPool.returnObject("b", o6);
+ if (_expectedObject != null && !_expectedObject.equals(obj)) {
+ _exception = new Exception("Expected: "+_expectedObject+ " found: "+obj);
+ _failed = true;
+ _complete = true;
+ break;
+ }
- assertNotSame(o1, o6);
- assertNotSame(o2, o6);
+ try {
+ Thread.sleep(_randomDelay ? _random.nextInt(_holdTime) : _holdTime);
+ } catch(final InterruptedException e) {
+ // ignored
+ }
+ try {
+ _pool.returnObject(key,obj);
+ } catch(final Exception e) {
+ _exception = e;
+ _failed = true;
+ _complete = true;
+ break;
+ }
+ }
+ _complete = true;
+ }
+ }
- // second a is still in there
- final String o7 = gkoPool.borrowObject("a");
- assertNotNull(o7);
- gkoPool.returnObject("a", o7);
+ /*
+ * Very simple test thread that just tries to borrow an object from
+ * the provided pool with the specified key and returns it after a wait
+ */
+ static class WaitingTestThread extends Thread {
+ private final KeyedObjectPool<String,String> _pool;
+ private final String _key;
+ private final long _pause;
+ private Throwable _thrown;
- assertSame(o4, o7);
- }
+ private long preBorrowMillis; // just before borrow
+ private long postBorrowMillis; // borrow returned
+ private long postReturnMillis; // after object was returned
+ private long endedMillis;
+ private String objectId;
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testSettersAndGetters() throws Exception {
- {
- gkoPool.setMaxTotalPerKey(123);
- assertEquals(123, gkoPool.getMaxTotalPerKey());
- }
- {
- gkoPool.setMaxIdlePerKey(12);
- assertEquals(12, gkoPool.getMaxIdlePerKey());
- }
- {
- gkoPool.setMaxWaitMillis(1234L);
- assertEquals(1234L, gkoPool.getMaxWaitMillis());
- }
- {
- gkoPool.setMinEvictableIdleTimeMillis(12345L);
- assertEquals(12345L, gkoPool.getMinEvictableIdleTimeMillis());
- }
- {
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(12345L));
- assertEquals(12345L, gkoPool.getMinEvictableIdleTime().toMillis());
- }
- {
- gkoPool.setNumTestsPerEvictionRun(11);
- assertEquals(11, gkoPool.getNumTestsPerEvictionRun());
- }
- {
- gkoPool.setTestOnBorrow(true);
- assertTrue(gkoPool.getTestOnBorrow());
- gkoPool.setTestOnBorrow(false);
- assertTrue(!gkoPool.getTestOnBorrow());
- }
- {
- gkoPool.setTestOnReturn(true);
- assertTrue(gkoPool.getTestOnReturn());
- gkoPool.setTestOnReturn(false);
- assertTrue(!gkoPool.getTestOnReturn());
- }
- {
- gkoPool.setTestWhileIdle(true);
- assertTrue(gkoPool.getTestWhileIdle());
- gkoPool.setTestWhileIdle(false);
- assertTrue(!gkoPool.getTestWhileIdle());
- }
- {
- gkoPool.setTimeBetweenEvictionRunsMillis(11235L);
- assertEquals(11235L, gkoPool.getTimeBetweenEvictionRunsMillis());
- }
- {
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(11235L));
- assertEquals(11235L, gkoPool.getTimeBetweenEvictionRuns().toMillis());
- }
- {
- gkoPool.setBlockWhenExhausted(true);
- assertTrue(gkoPool.getBlockWhenExhausted());
- gkoPool.setBlockWhenExhausted(false);
- assertFalse(gkoPool.getBlockWhenExhausted());
+ public WaitingTestThread(final KeyedObjectPool<String,String> pool, final String key, final long pause) {
+ _pool = pool;
+ _key = key;
+ _pause = pause;
+ _thrown = null;
}
- }
- /**
- * JIRA: POOL-270 - make sure constructor correctly sets run
- * frequency of evictor timer.
- */
- @Test
- public void testContructorEvictionConfig() throws Exception {
- final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
- config.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
- config.setMinEvictableIdleTime(Duration.ofMillis(50));
- config.setNumTestsPerEvictionRun(5);
- try (final GenericKeyedObjectPool<String, String> p = new GenericKeyedObjectPool<>(simpleFactory, config)) {
- for (int i = 0; i < 5; i++) {
- p.addObject("one");
- }
- try {
- Thread.sleep(100);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertEquals(5, p.getNumIdle("one"));
+ @Override
+ public void run() {
try {
- Thread.sleep(500);
- } catch (final InterruptedException e) {
- // ignore
+ preBorrowMillis = System.currentTimeMillis();
+ final String obj = _pool.borrowObject(_key);
+ objectId = obj;
+ postBorrowMillis = System.currentTimeMillis();
+ Thread.sleep(_pause);
+ _pool.returnObject(_key, obj);
+ postReturnMillis = System.currentTimeMillis();
+ } catch (final Exception e) {
+ _thrown = e;
+ } finally{
+ endedMillis = System.currentTimeMillis();
}
- assertEquals(0, p.getNumIdle("one"));
}
}
+ private static final Integer KEY_ZERO = Integer.valueOf(0);
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEviction() throws Exception {
- gkoPool.setMaxIdlePerKey(500);
- gkoPool.setMaxTotalPerKey(500);
- gkoPool.setNumTestsPerEvictionRun(100);
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(250));
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
-
- final String[] active = new String[500];
- for(int i=0;i<500;i++) {
- active[i] = gkoPool.borrowObject("");
- }
- for(int i=0;i<500;i++) {
- gkoPool.returnObject("",active[i]);
- }
+ private static final Integer KEY_ONE = Integer.valueOf(1);
- try { Thread.sleep(1000L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 300,"Should be less than 300 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 100 , "Should be less than 100 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertEquals(0,gkoPool.getNumIdle(""),"Should be zero idle, found " + gkoPool.getNumIdle(""));
+ private static final Integer KEY_TWO = Integer.valueOf(2);
- for(int i=0;i<500;i++) {
- active[i] = gkoPool.borrowObject("");
- }
- for(int i=0;i<500;i++) {
- gkoPool.returnObject("",active[i]);
- }
+ private static final boolean DISPLAY_THREAD_DETAILS=
+ Boolean.valueOf(System.getProperty("TestGenericKeyedObjectPool.display.thread.details", "false")).booleanValue();
+ // 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
- try { Thread.sleep(1000L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 500,"Should be less than 500 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 400,"Should be less than 400 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 300,"Should be less than 300 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 200,"Should be less than 200 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle("") < 100,"Should be less than 100 idle, found " + gkoPool.getNumIdle(""));
- try { Thread.sleep(600L); } catch(final InterruptedException e) { }
- assertEquals(0,gkoPool.getNumIdle(""),"Should be zero idle, found " + gkoPool.getNumIdle(""));
- }
+ /** setUp(): {@code new GenericKeyedObjectPool<String,String>(factory)} */
+ private GenericKeyedObjectPool<String,String> gkoPool = null;
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEviction2() throws Exception {
- gkoPool.setMaxIdlePerKey(500);
- gkoPool.setMaxTotalPerKey(500);
- gkoPool.setNumTestsPerEvictionRun(100);
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(500));
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+ /** setUp(): {@code new SimpleFactory<String>()} */
+ private SimpleFactory<String> simpleFactory = null;
- final String[] active = new String[500];
- final String[] active2 = new String[500];
- for (int i = 0; i < 500; i++) {
- active[i] = gkoPool.borrowObject("");
- active2[i] = gkoPool.borrowObject("2");
- }
- for (int i = 0; i < 500; i++) {
- gkoPool.returnObject("", active[i]);
- gkoPool.returnObject("2", active2[i]);
- }
-
- try {
- Thread.sleep(1100L);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 1000, "Should be less than 1000 idle, found " + gkoPool.getNumIdle());
- final long sleepMillisPart2 = 600L;
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 900, "Should be less than 900 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 800, "Should be less than 800 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 700, "Should be less than 700 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 600, "Should be less than 600 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 300, "Should be less than 300 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertTrue(gkoPool.getNumIdle() < 100, "Should be less than 100 idle, found " + gkoPool.getNumIdle());
- try {
- Thread.sleep(sleepMillisPart2);
- } catch (final InterruptedException e) {
- // ignore
- }
- assertEquals(0, gkoPool.getNumIdle(), "Should be zero idle, found " + gkoPool.getNumIdle());
- }
+ private void checkEvictionOrder(final boolean lifo) throws Exception {
+ final SimpleFactory<Integer> intFactory = new SimpleFactory<>();
+ try (final GenericKeyedObjectPool<Integer, String> intPool = new GenericKeyedObjectPool<>(intFactory)) {
+ intPool.setNumTestsPerEvictionRun(2);
+ intPool.setMinEvictableIdleTime(Duration.ofMillis(100));
+ intPool.setLifo(lifo);
- /**
- * Kicks off {@code numThreads} test threads, each of which will go
- * through {@code iterations} borrow-return cycles with random delay
- * times <= delay in between.
- *
- * @param <T> Type of object in pool
- * @param numThreads Number of test threads
- * @param iterations Number of iterations for each thread
- * @param delay Maximum delay between iterations
- * @param gkopPool The keyed object pool to use
- */
- public <T> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericKeyedObjectPool<String,T> gkopPool) {
- final ArrayList<TestThread<T>> threads = new ArrayList<>();
- for(int i=0;i<numThreads;i++) {
- final TestThread<T> testThread = new TestThread<>(gkopPool, iterations, delay);
- threads.add(testThread);
- final Thread t = new Thread(testThread);
- t.start();
- }
- for (final TestThread<T> testThread : threads) {
- while(!(testThread.complete())) {
- try {
- Thread.sleep(500L);
- } catch(final InterruptedException e) {
- // ignored
+ for (int i = 0; i < 3; i++) {
+ final Integer key = Integer.valueOf(i);
+ for (int j = 0; j < 5; j++) {
+ intPool.addObject(key);
}
}
- if(testThread.failed()) {
- fail("Thread failed: " + threads.indexOf(testThread) + "\n" +
- getExceptionTrace(testThread._exception));
- }
- }
- }
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testThreaded1() throws Exception {
- gkoPool.setMaxTotalPerKey(15);
- gkoPool.setMaxIdlePerKey(15);
- gkoPool.setMaxWaitMillis(1000L);
- runTestThreads(20, 100, 50, gkoPool);
- }
+ // Make all evictable
+ Thread.sleep(200);
- /**
- * Verifies that maxTotal is not exceeded when factory destroyObject
- * has high latency, testOnReturn is set and there is high incidence of
- * validation failures.
- *
- * @throws Exception May occur in some failure modes
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMaxTotalInvariant() throws Exception {
- final int maxTotal = 15;
- simpleFactory.setEvenValid(false); // Every other validation fails
- simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms
- simpleFactory.setMaxTotalPerKey(maxTotal); // (makes - destroys) bound
- simpleFactory.setValidationEnabled(true);
- gkoPool.setMaxTotal(maxTotal);
- gkoPool.setMaxIdlePerKey(-1);
- gkoPool.setTestOnReturn(true);
- gkoPool.setMaxWaitMillis(10000L);
- runTestThreads(5, 10, 50, gkoPool);
- }
+ /*
+ * Initial state (Key, Object) pairs in order of age:
+ *
+ * (0,0), (0,1), (0,2), (0,3), (0,4) (1,5), (1,6), (1,7), (1,8), (1,9) (2,10), (2,11), (2,12), (2,13),
+ * (2,14)
+ */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMinIdle() throws Exception {
- gkoPool.setMaxIdlePerKey(500);
- gkoPool.setMinIdlePerKey(5);
- gkoPool.setMaxTotalPerKey(10);
- gkoPool.setNumTestsPerEvictionRun(0);
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- gkoPool.setTestWhileIdle(true);
+ intPool.evict(); // Kill (0,0),(0,1)
+ assertEquals(3, intPool.getNumIdle(KEY_ZERO));
+ final String objZeroA = intPool.borrowObject(KEY_ZERO);
+ assertTrue(lifo ? objZeroA.equals("04") : objZeroA.equals("02"));
+ assertEquals(2, intPool.getNumIdle(KEY_ZERO));
+ final String objZeroB = intPool.borrowObject(KEY_ZERO);
+ assertTrue(objZeroB.equals("03"));
+ assertEquals(1, intPool.getNumIdle(KEY_ZERO));
+ intPool.evict(); // Kill remaining 0 survivor and (1,5)
+ assertEquals(0, intPool.getNumIdle(KEY_ZERO));
+ assertEquals(4, intPool.getNumIdle(KEY_ONE));
+ final String objOneA = intPool.borrowObject(KEY_ONE);
+ assertTrue(lifo ? objOneA.equals("19") : objOneA.equals("16"));
+ assertEquals(3, intPool.getNumIdle(KEY_ONE));
+ final String objOneB = intPool.borrowObject(KEY_ONE);
+ assertTrue(lifo ? objOneB.equals("18") : objOneB.equals("17"));
+ assertEquals(2, intPool.getNumIdle(KEY_ONE));
- //Generate a random key
- final String key = "A";
+ intPool.evict(); // Kill remaining 1 survivors
+ assertEquals(0, intPool.getNumIdle(KEY_ONE));
+ intPool.evict(); // Kill (2,10), (2,11)
+ assertEquals(3, intPool.getNumIdle(KEY_TWO));
+ final String objTwoA = intPool.borrowObject(KEY_TWO);
+ assertTrue(lifo ? objTwoA.equals("214") : objTwoA.equals("212"));
+ assertEquals(2, intPool.getNumIdle(KEY_TWO));
+ intPool.evict(); // All dead now
+ assertEquals(0, intPool.getNumIdle(KEY_TWO));
- gkoPool.preparePool(key);
+ intPool.evict(); // Should do nothing - make sure no exception
+ // Currently 2 zero, 2 one and 1 two active. Return them
+ intPool.returnObject(KEY_ZERO, objZeroA);
+ intPool.returnObject(KEY_ZERO, objZeroB);
+ intPool.returnObject(KEY_ONE, objOneA);
+ intPool.returnObject(KEY_ONE, objOneB);
+ intPool.returnObject(KEY_TWO, objTwoA);
+ // Remove all idle objects
+ intPool.clear();
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ // Reload
+ intPool.setMinEvictableIdleTime(Duration.ofMillis(500));
+ intFactory.counter = 0; // Reset counter
+ for (int i = 0; i < 3; i++) {
+ final Integer key = Integer.valueOf(i);
+ for (int j = 0; j < 5; j++) {
+ intPool.addObject(key);
+ }
+ Thread.sleep(200);
+ }
- final String[] active = new String[5];
- active[0] = gkoPool.borrowObject(key);
+ // 0's are evictable, others not
+ intPool.evict(); // Kill (0,0),(0,1)
+ assertEquals(3, intPool.getNumIdle(KEY_ZERO));
+ intPool.evict(); // Kill (0,2),(0,3)
+ assertEquals(1, intPool.getNumIdle(KEY_ZERO));
+ intPool.evict(); // Kill (0,4), leave (1,5)
+ assertEquals(0, intPool.getNumIdle(KEY_ZERO));
+ assertEquals(5, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ intPool.evict(); // (1,6), (1,7)
+ assertEquals(5, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ intPool.evict(); // (1,8), (1,9)
+ assertEquals(5, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ intPool.evict(); // (2,10), (2,11)
+ assertEquals(5, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ intPool.evict(); // (2,12), (2,13)
+ assertEquals(5, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ intPool.evict(); // (2,14), (1,5)
+ assertEquals(5, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ Thread.sleep(200); // Ones now timed out
+ intPool.evict(); // kill (1,6), (1,7) - (1,5) missed
+ assertEquals(3, intPool.getNumIdle(KEY_ONE));
+ assertEquals(5, intPool.getNumIdle(KEY_TWO));
+ final String obj = intPool.borrowObject(KEY_ONE);
+ if (lifo) {
+ assertEquals("19", obj);
+ } else {
+ assertEquals("15", obj);
+ }
+ }
+ }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ private void checkEvictorVisiting(final boolean lifo) throws Exception {
+ VisitTrackerFactory<Integer> trackerFactory = new VisitTrackerFactory<>();
+ try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(
+ trackerFactory)) {
+ intPool.setNumTestsPerEvictionRun(2);
+ intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
+ intPool.setTestWhileIdle(true);
+ intPool.setLifo(lifo);
+ intPool.setTestOnReturn(false);
+ intPool.setTestOnBorrow(false);
+ for (int i = 0; i < 3; i++) {
+ trackerFactory.resetId();
+ final Integer key = Integer.valueOf(i);
+ for (int j = 0; j < 8; j++) {
+ intPool.addObject(key);
+ }
+ }
+ intPool.evict(); // Visit oldest 2 - 00 and 01
+ VisitTracker<Integer> obj = intPool.borrowObject(KEY_ZERO);
+ intPool.returnObject(KEY_ZERO, obj);
+ obj = intPool.borrowObject(KEY_ZERO);
+ intPool.returnObject(KEY_ZERO, obj);
+ // borrow, return, borrow, return
+ // FIFO will move 0 and 1 to end - 2,3,4,5,6,7,0,1
+ // LIFO, 7 out, then in, then out, then in - 7,6,5,4,3,2,1,0
+ intPool.evict(); // Should visit 02 and 03 in either case
+ for (int i = 0; i < 8; i++) {
+ final VisitTracker<Integer> tracker = intPool.borrowObject(KEY_ZERO);
+ 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.");
+ }
+ }
+ // 0's are all out
- for(int i=1 ; i<5 ; i++) {
- active[i] = gkoPool.borrowObject(key);
- }
+ intPool.setNumTestsPerEvictionRun(3);
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ intPool.evict(); // 10, 11, 12
+ intPool.evict(); // 13, 14, 15
- for(int i=0 ; i<5 ; i++) {
- gkoPool.returnObject(key, active[i]);
+ obj = intPool.borrowObject(KEY_ONE);
+ intPool.returnObject(KEY_ONE, obj);
+ obj = intPool.borrowObject(KEY_ONE);
+ intPool.returnObject(KEY_ONE, obj);
+ obj = intPool.borrowObject(KEY_ONE);
+ intPool.returnObject(KEY_ONE, 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
+ intPool.evict();
+ // LIFO - 16, 17, 20
+ // FIFO - 16, 17, 10
+ intPool.evict();
+ // LIFO - 21, 22, 23
+ // FIFO - 11, 12, 20
+ intPool.evict();
+ // LIFO - 24, 25, 26
+ // FIFO - 21, 22, 23
+ intPool.evict();
+ // LIFO - 27, 10, 11
+ // 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)) {
+ 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.");
+ }
+ }
}
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 10,"Should be 10 idle, found " + gkoPool.getNumIdle());
- }
+ // Randomly generate some 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 < smallPrimes.length; i++) {
+ for (int j = 0; j < 5; j++) {// Try the tests a few times
+ // Can't use clear as some objects are still active so create
+ // a new pool
+ trackerFactory = new VisitTrackerFactory<>();
+ try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(
+ trackerFactory)) {
+ intPool.setMaxIdlePerKey(-1);
+ intPool.setMaxTotalPerKey(-1);
+ intPool.setNumTestsPerEvictionRun(smallPrimes[i]);
+ intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
+ intPool.setTestWhileIdle(true);
+ intPool.setLifo(lifo);
+ intPool.setTestOnReturn(false);
+ intPool.setTestOnBorrow(false);
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMinIdleMaxTotalPerKey() throws Exception {
- gkoPool.setMaxIdlePerKey(500);
- gkoPool.setMinIdlePerKey(5);
- gkoPool.setMaxTotalPerKey(10);
- gkoPool.setNumTestsPerEvictionRun(0);
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- gkoPool.setTestWhileIdle(true);
+ final int zeroLength = 10 + random.nextInt(20);
+ for (int k = 0; k < zeroLength; k++) {
+ intPool.addObject(KEY_ZERO);
+ }
+ final int oneLength = 10 + random.nextInt(20);
+ for (int k = 0; k < oneLength; k++) {
+ intPool.addObject(KEY_ONE);
+ }
+ final int twoLength = 10 + random.nextInt(20);
+ for (int k = 0; k < twoLength; k++) {
+ intPool.addObject(KEY_TWO);
+ }
- final String key = "A";
+ // Choose a random number of evictor runs
+ final int runs = 10 + random.nextInt(50);
+ for (int k = 0; k < runs; k++) {
+ intPool.evict();
+ }
- gkoPool.preparePool(key);
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " +
- gkoPool.getNumIdle());
+ // Total instances in pool
+ final int totalInstances = zeroLength + oneLength + twoLength;
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ // Number of times evictor should have cycled through pools
+ final int cycleCount = (runs * intPool.getNumTestsPerEvictionRun()) / totalInstances;
- final String[] active = new String[10];
+ // Look at elements and make sure they are visited cycleCount
+ // or cycleCount + 1 times
+ VisitTracker<Integer> tracker = null;
+ int visitCount = 0;
+ for (int k = 0; k < zeroLength; k++) {
+ tracker = intPool.borrowObject(KEY_ZERO);
+ visitCount = tracker.getValidateCount();
+ if (visitCount < cycleCount || visitCount > cycleCount + 1) {
+ fail(formatSettings("ZERO", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k,
+ "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances",
+ totalInstances, zeroLength, oneLength, twoLength));
+ }
+ }
+ for (int k = 0; k < oneLength; k++) {
+ tracker = intPool.borrowObject(KEY_ONE);
+ visitCount = tracker.getValidateCount();
+ if (visitCount < cycleCount || visitCount > cycleCount + 1) {
+ fail(formatSettings("ONE", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount",
+ visitCount, "cycleCount", cycleCount, "totalInstances", totalInstances, zeroLength,
+ oneLength, twoLength));
+ }
+ }
+ final int visits[] = new int[twoLength];
+ for (int k = 0; k < twoLength; k++) {
+ tracker = intPool.borrowObject(KEY_TWO);
+ visitCount = tracker.getValidateCount();
+ visits[k] = visitCount;
+ if (visitCount < cycleCount || visitCount > cycleCount + 1) {
+ final StringBuilder sb = new StringBuilder("Visits:");
+ for (int l = 0; l <= k; l++) {
+ sb.append(visits[l]).append(' ');
+ }
+ fail(formatSettings("TWO " + sb.toString(), "runs", runs, "lifo", lifo, "i", i, "j", j, "k",
+ k, "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances",
+ totalInstances, zeroLength, oneLength, twoLength));
+ }
+ }
+ }
+ }
+ }
+ }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ private String formatSettings(final String title, final String s, final int i, final String s0, final boolean b0, final String s1, final int i1, final String s2, final int i2, final String s3, final int i3,
+ final String s4, final int i4, final String s5, final int i5, final String s6, final int i6, final int zeroLength, final int oneLength, final int twoLength){
+ final StringBuilder sb = new StringBuilder(80);
+ sb.append(title).append(' ');
+ sb.append(s).append('=').append(i).append(' ');
+ sb.append(s0).append('=').append(b0).append(' ');
+ sb.append(s1).append('=').append(i1).append(' ');
+ sb.append(s2).append('=').append(i2).append(' ');
+ sb.append(s3).append('=').append(i3).append(' ');
+ sb.append(s4).append('=').append(i4).append(' ');
+ sb.append(s5).append('=').append(i5).append(' ');
+ sb.append(s6).append('=').append(i6).append(' ');
+ sb.append("Lengths=").append(zeroLength).append(',').append(oneLength).append(',').append(twoLength).append(' ');
+ return sb.toString();
+ }
- for(int i=0 ; i<5 ; i++) {
- active[i] = gkoPool.borrowObject(key);
- }
+ private String getExceptionTrace(final Throwable t){
+ final StringWriter sw = new StringWriter();
+ t.printStackTrace(new PrintWriter(sw));
+ return sw.toString();
+ }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ @Override
+ protected Object getNthObject(final Object key, final int n) {
+ return String.valueOf(key) + String.valueOf(n);
+ }
- for(int i=0 ; i<5 ; i++) {
- gkoPool.returnObject(key, active[i]);
- }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 10,"Should be 10 idle, found " + gkoPool.getNumIdle());
+ @Override
+ protected boolean isFifo() {
+ return false;
+ }
- for(int i=0 ; i<10 ; i++) {
- active[i] = gkoPool.borrowObject(key);
- }
+ @Override
+ protected boolean isLifo() {
+ return true;
+ }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 0,"Should be 0 idle, found " + gkoPool.getNumIdle());
+ @Override
+ protected KeyedObjectPool<Object,Object> makeEmptyPool(final int minCapacity) {
+ final KeyedPooledObjectFactory<Object,Object> perKeyFactory =
+ new SimplePerKeyFactory();
+ final GenericKeyedObjectPool<Object,Object> perKeyPool =
+ new GenericKeyedObjectPool<>(perKeyFactory);
+ perKeyPool.setMaxTotalPerKey(minCapacity);
+ perKeyPool.setMaxIdlePerKey(minCapacity);
+ return perKeyPool;
+ }
- for(int i=0 ; i<10 ; i++) {
- gkoPool.returnObject(key, active[i]);
- }
+ @Override
+ protected KeyedObjectPool<Object,Object> makeEmptyPool(final KeyedPooledObjectFactory<Object,Object> fac) {
+ return new GenericKeyedObjectPool<>(fac);
+ }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 10,"Should be 10 idle, found " + gkoPool.getNumIdle());
+ @Override
+ protected Object makeKey(final int n) {
+ return String.valueOf(n);
}
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testMinIdleNoPreparePool() throws Exception {
- gkoPool.setMaxIdlePerKey(500);
- gkoPool.setMinIdlePerKey(5);
- gkoPool.setMaxTotalPerKey(10);
- gkoPool.setNumTestsPerEvictionRun(0);
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
- gkoPool.setTestWhileIdle(true);
-
-
- //Generate a random key
- final String key = "A";
+ /**
+ * Kicks off {@code numThreads} test threads, each of which will go
+ * through {@code iterations} borrow-return cycles with random delay
+ * times <= delay in between.
+ *
+ * @param <T> Type of object in pool
+ * @param numThreads Number of test threads
+ * @param iterations Number of iterations for each thread
+ * @param delay Maximum delay between iterations
+ * @param gkopPool The keyed object pool to use
+ */
+ public <T> void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericKeyedObjectPool<String,T> gkopPool) {
+ final ArrayList<TestThread<T>> threads = new ArrayList<>();
+ for(int i=0;i<numThreads;i++) {
+ final TestThread<T> testThread = new TestThread<>(gkopPool, iterations, delay);
+ threads.add(testThread);
+ final Thread t = new Thread(testThread);
+ t.start();
+ }
+ for (final TestThread<T> testThread : threads) {
+ while(!(testThread.complete())) {
+ try {
+ Thread.sleep(500L);
+ } catch(final InterruptedException e) {
+ // ignored
+ }
+ }
+ if(testThread.failed()) {
+ fail("Thread failed: " + threads.indexOf(testThread) + "\n" +
+ getExceptionTrace(testThread._exception));
+ }
+ }
+ }
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 0,"Should be 0 idle, found " + gkoPool.getNumIdle());
+ @BeforeEach
+ public void setUp() throws Exception {
+ simpleFactory = new SimpleFactory<>();
+ gkoPool = new GenericKeyedObjectPool<>(simpleFactory);
+ }
- final Object active = gkoPool.borrowObject(key);
- assertNotNull(active);
+ @Override
+ @AfterEach
+ public void tearDown() throws Exception {
+ super.tearDown();
+ final ObjectName jmxName = gkoPool.getJmxName();
+ final String poolName = Objects.toString(jmxName, null);
+ gkoPool.clear();
+ gkoPool.close();
+ gkoPool = null;
+ simpleFactory = null;
- try { Thread.sleep(150L); } catch(final InterruptedException e) { }
- assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ final Set<ObjectName> result = mbs.queryNames(new ObjectName(
+ "org.apache.commoms.pool2:type=GenericKeyedObjectPool,*"),
+ 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());
}
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testFIFO() throws Exception {
- gkoPool.setLifo(false);
- final String key = "key";
- gkoPool.addObject(key); // "key0"
- gkoPool.addObject(key); // "key1"
- gkoPool.addObject(key); // "key2"
- assertEquals( "key0", gkoPool.borrowObject(key),"Oldest");
- assertEquals( "key1", gkoPool.borrowObject(key),"Middle");
- assertEquals( "key2", gkoPool.borrowObject(key),"Youngest");
- final String s = gkoPool.borrowObject(key);
- assertEquals( "key3", s,"new-3");
- gkoPool.returnObject(key, s);
- assertEquals( s, gkoPool.borrowObject(key),"returned");
- assertEquals( "key4", gkoPool.borrowObject(key),"new-4");
+ public void testBlockedKeyDoesNotBlockPool() throws Exception {
+ gkoPool.setBlockWhenExhausted(true);
+ gkoPool.setMaxWaitMillis(5000);
+ gkoPool.setMaxTotalPerKey(1);
+ gkoPool.setMaxTotal(-1);
+ gkoPool.borrowObject("one");
+ 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();
+ // 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
+ Thread.sleep(1000);
+ gkoPool.borrowObject("two");
+ final long endMillis = System.currentTimeMillis();
+ // If it fails it will be more than 4000ms (5000 less the 1000 sleep)
+ // 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,
+ "Elapsed time: " + (endMillis - startMillis) + " should be less than 4000");
+
}
+ /*
+ * 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"
+ })
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testLIFO() throws Exception {
- gkoPool.setLifo(true);
- final String key = "key";
- gkoPool.addObject(key); // "key0"
- gkoPool.addObject(key); // "key1"
- gkoPool.addObject(key); // "key2"
- assertEquals( "key2", gkoPool.borrowObject(key),"Youngest");
- assertEquals( "key1", gkoPool.borrowObject(key),"Middle");
- assertEquals( "key0", gkoPool.borrowObject(key),"Oldest");
- final String s = gkoPool.borrowObject(key);
- assertEquals( "key3", s,"new-3");
- gkoPool.returnObject(key, s);
- assertEquals( s, gkoPool.borrowObject(key),"returned");
- assertEquals( "key4", gkoPool.borrowObject(key),"new-4");
+ public void testBorrowObjectFairness() throws Exception {
+
+ final int numThreads = 40;
+ final int maxTotal = 40;
+
+ final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
+ config.setMaxTotalPerKey(maxTotal);
+ config.setFairness(true);
+ config.setLifo(false);
+ config.setMaxIdlePerKey(maxTotal);
+
+ gkoPool = new GenericKeyedObjectPool<>(simpleFactory, config);
+
+ // Exhaust the pool
+ final String[] objects = new String[maxTotal];
+ for (int i = 0; i < maxTotal; i++) {
+ objects[i] = gkoPool.borrowObject("0");
+ }
+
+ // 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<>(gkoPool, 1, 0, 2000, false, "0" + String.valueOf(i % maxTotal), "0");
+ 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++) {
+ gkoPool.returnObject("0", objects[i]);
+ }
+
+ // Wait for threads to finish
+ for(int i=0;i<numThreads;i++) {
+ while(!(threads[i]).complete()) {
+ try {
+ Thread.sleep(500L);
+ } catch(final InterruptedException e) {
+ // ignored
+ }
+ }
+ if(threads[i].failed()) {
+ fail("Thread "+i+" failed: "+threads[i]._exception.toString());
+ }
+ }
}
/**
- * Test to make sure evictor visits least recently used objects first,
- * regardless of FIFO/LIFO
- *
- * JIRA: POOL-86
+ * POOL-192
+ * Verify that clear(key) does not leak capacity.
*
* @throws Exception May occur in some failure modes
*/
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictionOrder() throws Exception {
- checkEvictionOrder(false);
- checkEvictionOrder(true);
+ public void testClear() throws Exception {
+ gkoPool.setMaxTotal(2);
+ gkoPool.setMaxTotalPerKey(2);
+ gkoPool.setBlockWhenExhausted(false);
+ gkoPool.addObject("one");
+ gkoPool.addObject("one");
+ assertEquals(2, gkoPool.getNumIdle());
+ gkoPool.clear("one");
+ assertEquals(0, gkoPool.getNumIdle());
+ assertEquals(0, gkoPool.getNumIdle("one"));
+ final String obj1 = gkoPool.borrowObject("one");
+ final String obj2 = gkoPool.borrowObject("one");
+ gkoPool.returnObject("one", obj1);
+ gkoPool.returnObject("one", obj2);
+ gkoPool.clear();
+ assertEquals(0, gkoPool.getNumIdle());
+ assertEquals(0, gkoPool.getNumIdle("one"));
+ gkoPool.borrowObject("one");
+ gkoPool.borrowObject("one");
+ gkoPool.close();
}
- private void checkEvictionOrder(final boolean lifo) throws Exception {
- final SimpleFactory<Integer> intFactory = new SimpleFactory<>();
- try (final GenericKeyedObjectPool<Integer, String> intPool = new GenericKeyedObjectPool<>(intFactory)) {
- intPool.setNumTestsPerEvictionRun(2);
- intPool.setMinEvictableIdleTime(Duration.ofMillis(100));
- intPool.setLifo(lifo);
-
- for (int i = 0; i < 3; i++) {
- final Integer key = Integer.valueOf(i);
+ /**
+ * Test to make sure that clearOldest does not destroy instances that have been checked out.
+ *
+ * @throws Exception May occur in some failure modes
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testClearOldest() throws Exception {
+ // Make destroy have some latency so clearOldest takes some time
+ final WaiterFactory<String> waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 50, 5, 0);
+ try (final GenericKeyedObjectPool<String, Waiter> waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) {
+ waiterPool.setMaxTotalPerKey(5);
+ waiterPool.setMaxTotal(50);
+ waiterPool.setLifo(false);
+ // Load the pool with idle instances - 5 each for 10 keys
+ for (int i = 0; i < 10; i++) {
+ final String key = Integer.toString(i);
for (int j = 0; j < 5; j++) {
- intPool.addObject(key);
+ waiterPool.addObject(key);
}
+ // Make sure order is maintained
+ Thread.sleep(20);
}
+ // Now set up a race - one thread wants a new instance, triggering clearOldest
+ // Other goes after an element on death row
+ // See if we end up with dead man walking
+ final SimpleTestThread<Waiter> t2 = new SimpleTestThread<>(waiterPool, "51");
+ final Thread thread2 = new Thread(t2);
+ thread2.start(); // Triggers clearOldest, killing all of the 0's and the 2 oldest 1's
+ Thread.sleep(50); // Wait for clearOldest to kick off, but not long enough to reach the 1's
+ final Waiter waiter = waiterPool.borrowObject("1");
+ Thread.sleep(200); // Wait for execution to happen
+ waiterPool.returnObject("1", waiter); // Will throw IllegalStateException if dead
+ }
+ }
- // Make all evictable
- Thread.sleep(200);
-
- /*
- * Initial state (Key, Object) pairs in order of age:
- *
- * (0,0), (0,1), (0,2), (0,3), (0,4) (1,5), (1,6), (1,7), (1,8), (1,9) (2,10), (2,11), (2,12), (2,13),
- * (2,14)
- */
-
- intPool.evict(); // Kill (0,0),(0,1)
- assertEquals(3, intPool.getNumIdle(KEY_ZERO));
- final String objZeroA = intPool.borrowObject(KEY_ZERO);
- assertTrue(lifo ? objZeroA.equals("04") : objZeroA.equals("02"));
- assertEquals(2, intPool.getNumIdle(KEY_ZERO));
- final String objZeroB = intPool.borrowObject(KEY_ZERO);
- assertTrue(objZeroB.equals("03"));
- assertEquals(1, intPool.getNumIdle(KEY_ZERO));
-
- intPool.evict(); // Kill remaining 0 survivor and (1,5)
- assertEquals(0, intPool.getNumIdle(KEY_ZERO));
- assertEquals(4, intPool.getNumIdle(KEY_ONE));
- final String objOneA = intPool.borrowObject(KEY_ONE);
- assertTrue(lifo ? objOneA.equals("19") : objOneA.equals("16"));
- assertEquals(3, intPool.getNumIdle(KEY_ONE));
- final String objOneB = intPool.borrowObject(KEY_ONE);
- assertTrue(lifo ? objOneB.equals("18") : objOneB.equals("17"));
- assertEquals(2, intPool.getNumIdle(KEY_ONE));
-
- intPool.evict(); // Kill remaining 1 survivors
- assertEquals(0, intPool.getNumIdle(KEY_ONE));
- intPool.evict(); // Kill (2,10), (2,11)
- assertEquals(3, intPool.getNumIdle(KEY_TWO));
- final String objTwoA = intPool.borrowObject(KEY_TWO);
- assertTrue(lifo ? objTwoA.equals("214") : objTwoA.equals("212"));
- assertEquals(2, intPool.getNumIdle(KEY_TWO));
- intPool.evict(); // All dead now
- assertEquals(0, intPool.getNumIdle(KEY_TWO));
-
- intPool.evict(); // Should do nothing - make sure no exception
- // Currently 2 zero, 2 one and 1 two active. Return them
- intPool.returnObject(KEY_ZERO, objZeroA);
- intPool.returnObject(KEY_ZERO, objZeroB);
- intPool.returnObject(KEY_ONE, objOneA);
- intPool.returnObject(KEY_ONE, objOneB);
- intPool.returnObject(KEY_TWO, objTwoA);
- // Remove all idle objects
- intPool.clear();
-
- // Reload
- intPool.setMinEvictableIdleTime(Duration.ofMillis(500));
- intFactory.counter = 0; // Reset counter
- for (int i = 0; i < 3; i++) {
- final Integer key = Integer.valueOf(i);
- for (int j = 0; j < 5; j++) {
- intPool.addObject(key);
- }
- Thread.sleep(200);
- }
- // 0's are evictable, others not
- intPool.evict(); // Kill (0,0),(0,1)
- assertEquals(3, intPool.getNumIdle(KEY_ZERO));
- intPool.evict(); // Kill (0,2),(0,3)
- assertEquals(1, intPool.getNumIdle(KEY_ZERO));
- intPool.evict(); // Kill (0,4), leave (1,5)
- assertEquals(0, intPool.getNumIdle(KEY_ZERO));
- assertEquals(5, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- intPool.evict(); // (1,6), (1,7)
- assertEquals(5, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- intPool.evict(); // (1,8), (1,9)
- assertEquals(5, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- intPool.evict(); // (2,10), (2,11)
- assertEquals(5, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- intPool.evict(); // (2,12), (2,13)
- assertEquals(5, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- intPool.evict(); // (2,14), (1,5)
- assertEquals(5, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- Thread.sleep(200); // Ones now timed out
- intPool.evict(); // kill (1,6), (1,7) - (1,5) missed
- assertEquals(3, intPool.getNumIdle(KEY_ONE));
- assertEquals(5, intPool.getNumIdle(KEY_TWO));
- final String obj = intPool.borrowObject(KEY_ONE);
- if (lifo) {
- assertEquals("19", obj);
- } else {
- assertEquals("15", obj);
- }
+ // POOL-259
+ @Test
+ public void testClientWaitStats() throws Exception {
+ final SimpleFactory<String> factory = new SimpleFactory<>();
+ // Give makeObject a little latency
+ factory.setMakeLatency(200);
+ try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory,
+ new GenericKeyedObjectPoolConfig<String>())) {
+ final String s = pool.borrowObject("one");
+ // 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("one", s);
+ pool.borrowObject("one");
+ // 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);
}
}
-
/**
- * Verifies that the evictor visits objects in expected order
- * and frequency.
+ * POOL-231 - verify that concurrent invalidates of the same object do not
+ * corrupt pool destroyCount.
*
* @throws Exception May occur in some failure modes
*/
@Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testEvictorVisiting() throws Exception {
- checkEvictorVisiting(true);
- checkEvictorVisiting(false);
- }
-
- private void checkEvictorVisiting(final boolean lifo) throws Exception {
- VisitTrackerFactory<Integer> trackerFactory = new VisitTrackerFactory<>();
- try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(
- trackerFactory)) {
- intPool.setNumTestsPerEvictionRun(2);
- intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
- intPool.setTestWhileIdle(true);
- intPool.setLifo(lifo);
- intPool.setTestOnReturn(false);
- intPool.setTestOnBorrow(false);
- for (int i = 0; i < 3; i++) {
- trackerFactory.resetId();
- final Integer key = Integer.valueOf(i);
- for (int j = 0; j < 8; j++) {
- intPool.addObject(key);
- }
+ public void testConcurrentInvalidate() throws Exception {
+ // Get allObjects and idleObjects loaded with some instances
+ final int nObjects = 1000;
+ final String key = "one";
+ gkoPool.setMaxTotal(nObjects);
+ gkoPool.setMaxTotalPerKey(nObjects);
+ gkoPool.setMaxIdlePerKey(nObjects);
+ final String [] obj = new String[nObjects];
+ for (int i = 0; i < nObjects; i++) {
+ obj[i] = gkoPool.borrowObject(key);
+ }
+ for (int i = 0; i < nObjects; i++) {
+ if (i % 2 == 0) {
+ gkoPool.returnObject(key, obj[i]);
}
- intPool.evict(); // Visit oldest 2 - 00 and 01
- VisitTracker<Integer> obj = intPool.borrowObject(KEY_ZERO);
- intPool.returnObject(KEY_ZERO, obj);
- obj = intPool.borrowObject(KEY_ZERO);
- intPool.returnObject(KEY_ZERO, obj);
- // borrow, return, borrow, return
- // FIFO will move 0 and 1 to end - 2,3,4,5,6,7,0,1
- // LIFO, 7 out, then in, then out, then in - 7,6,5,4,3,2,1,0
- intPool.evict(); // Should visit 02 and 03 in either case
- for (int i = 0; i < 8; i++) {
- final VisitTracker<Integer> tracker = intPool.borrowObject(KEY_ZERO);
- 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.");
- }
+ }
+ 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));
}
- // 0's are all out
-
- intPool.setNumTestsPerEvictionRun(3);
-
- intPool.evict(); // 10, 11, 12
- intPool.evict(); // 13, 14, 15
-
- obj = intPool.borrowObject(KEY_ONE);
- intPool.returnObject(KEY_ONE, obj);
- obj = intPool.borrowObject(KEY_ONE);
- intPool.returnObject(KEY_ONE, obj);
- obj = intPool.borrowObject(KEY_ONE);
- intPool.returnObject(KEY_ONE, 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
- intPool.evict();
- // LIFO - 16, 17, 20
- // FIFO - 16, 17, 10
- intPool.evict();
- // LIFO - 21, 22, 23
- // FIFO - 11, 12, 20
- intPool.evict();
- // LIFO - 24, 25, 26
- // FIFO - 21, 22, 23
- intPool.evict();
- // LIFO - 27, 10, 11
- // 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)) {
- 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.");
+ targets.add(targ);
+ // Launch nThreads threads all trying to invalidate the target
+ for (int i = 0; i < nThreads; i++) {
+ threads[i] =
+ new InvalidateThread(gkoPool,key, 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, gkoPool.getDestroyedCount());
+ }
- // Randomly generate some 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 < smallPrimes.length; i++) {
- for (int j = 0; j < 5; j++) {// Try the tests a few times
- // Can't use clear as some objects are still active so create
- // a new pool
- trackerFactory = new VisitTrackerFactory<>();
- try (GenericKeyedObjectPool<Integer, VisitTracker<Integer>> intPool = new GenericKeyedObjectPool<>(
- trackerFactory)) {
- intPool.setMaxIdlePerKey(-1);
- intPool.setMaxTotalPerKey(-1);
- intPool.setNumTestsPerEvictionRun(smallPrimes[i]);
- intPool.setMinEvictableIdleTime(Duration.ofMillis(-1));
- intPool.setTestWhileIdle(true);
- intPool.setLifo(lifo);
- intPool.setTestOnReturn(false);
- intPool.setTestOnBorrow(false);
-
- final int zeroLength = 10 + random.nextInt(20);
- for (int k = 0; k < zeroLength; k++) {
- intPool.addObject(KEY_ZERO);
- }
- final int oneLength = 10 + random.nextInt(20);
- for (int k = 0; k < oneLength; k++) {
- intPool.addObject(KEY_ONE);
- }
- final int twoLength = 10 + random.nextInt(20);
- for (int k = 0; k < twoLength; k++) {
- intPool.addObject(KEY_TWO);
- }
+ @Test
+ public void testConstructorNullFactory() {
+ // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
+ assertThrows(IllegalArgumentException.class,
+ () -> new GenericKeyedObjectPool<>(null));
+ }
- // Choose a random number of evictor runs
- final int runs = 10 + random.nextInt(50);
- for (int k = 0; k < runs; k++) {
- intPool.evict();
- }
+ @SuppressWarnings("deprecation")
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testConstructors() throws Exception {
- // Total instances in pool
- final int totalInstances = zeroLength + oneLength + twoLength;
+ // Make constructor arguments all different from defaults
+ final int maxTotalPerKey = 1;
+ final int minIdle = 2;
+ final long maxWait = 3;
+ final int maxIdle = 4;
+ final int maxTotal = 5;
+ final long minEvictableIdleTimeMillis = 6;
+ 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 KeyedPooledObjectFactory<Object, Object> dummyFactory = new DummyFactory();
- // Number of times evictor should have cycled through pools
- final int cycleCount = (runs * intPool.getNumTestsPerEvictionRun()) / totalInstances;
+ try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory)) {
+ assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY, objPool.getMaxTotalPerKey());
+ assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY, objPool.getMaxIdlePerKey());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, objPool.getMaxWaitMillis());
+ assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY, objPool.getMinIdlePerKey());
+ assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL, objPool.getMaxTotal());
+ //
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
+ objPool.getMinEvictableIdleTimeMillis());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
+ objPool.getMinEvictableIdleTime());
+ //
+ assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, objPool.getNumTestsPerEvictionRun());
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW),
+ Boolean.valueOf(objPool.getTestOnBorrow()));
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN),
+ Boolean.valueOf(objPool.getTestOnReturn()));
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE),
+ Boolean.valueOf(objPool.getTestWhileIdle()));
+ //
+ assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
+ objPool.getTimeBetweenEvictionRunsMillis());
+ assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
+ objPool.getTimeBetweenEvictionRuns());
+ //
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED),
+ Boolean.valueOf(objPool.getBlockWhenExhausted()));
+ assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(objPool.getLifo()));
+ }
- // Look at elements and make sure they are visited cycleCount
- // or cycleCount + 1 times
- VisitTracker<Integer> tracker = null;
- int visitCount = 0;
- for (int k = 0; k < zeroLength; k++) {
- tracker = intPool.borrowObject(KEY_ZERO);
- visitCount = tracker.getValidateCount();
- if (visitCount < cycleCount || visitCount > cycleCount + 1) {
- fail(formatSettings("ZERO", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k,
- "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances",
- totalInstances, zeroLength, oneLength, twoLength));
- }
- }
- for (int k = 0; k < oneLength; k++) {
- tracker = intPool.borrowObject(KEY_ONE);
- visitCount = tracker.getValidateCount();
- if (visitCount < cycleCount || visitCount > cycleCount + 1) {
- fail(formatSettings("ONE", "runs", runs, "lifo", lifo, "i", i, "j", j, "k", k, "visitCount",
- visitCount, "cycleCount", cycleCount, "totalInstances", totalInstances, zeroLength,
- oneLength, twoLength));
- }
- }
- final int visits[] = new int[twoLength];
- for (int k = 0; k < twoLength; k++) {
- tracker = intPool.borrowObject(KEY_TWO);
- visitCount = tracker.getValidateCount();
- visits[k] = visitCount;
- if (visitCount < cycleCount || visitCount > cycleCount + 1) {
- final StringBuilder sb = new StringBuilder("Visits:");
- for (int l = 0; l <= k; l++) {
- sb.append(visits[l]).append(' ');
- }
- fail(formatSettings("TWO " + sb.toString(), "runs", runs, "lifo", lifo, "i", i, "j", j, "k",
- k, "visitCount", visitCount, "cycleCount", cycleCount, "totalInstances",
- totalInstances, zeroLength, oneLength, twoLength));
- }
- }
- }
+ final GenericKeyedObjectPoolConfig<Object> config = new GenericKeyedObjectPoolConfig<>();
+ config.setLifo(lifo);
+ config.setMaxTotalPerKey(maxTotalPerKey);
+ config.setMaxIdlePerKey(maxIdle);
+ config.setMinIdlePerKey(minIdle);
+ config.setMaxTotal(maxTotal);
+ config.setMaxWaitMillis(maxWait);
+ config.setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis));
+ config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
+ config.setTestOnBorrow(testOnBorrow);
+ config.setTestOnReturn(testOnReturn);
+ config.setTestWhileIdle(testWhileIdle);
+ config.setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
+ config.setBlockWhenExhausted(blockWhenExhausted);
+ try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory, config)) {
+ assertEquals(maxTotalPerKey, objPool.getMaxTotalPerKey());
+ assertEquals(maxIdle, objPool.getMaxIdlePerKey());
+ assertEquals(maxWait, objPool.getMaxWaitMillis());
+ assertEquals(minIdle, objPool.getMinIdlePerKey());
+ assertEquals(maxTotal, objPool.getMaxTotal());
+ assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTimeMillis());
+ assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTime().toMillis());
+ assertEquals(numTestsPerEvictionRun, objPool.getNumTestsPerEvictionRun());
+ assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(objPool.getTestOnBorrow()));
+ assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(objPool.getTestOnReturn()));
+ assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(objPool.getTestWhileIdle()));
+ assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRunsMillis());
+ assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRuns().toMillis());
+ assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(objPool.getBlockWhenExhausted()));
+ assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(objPool.getLifo()));
+ }
+ }
+
+ /**
+ * JIRA: POOL-270 - make sure constructor correctly sets run
+ * frequency of evictor timer.
+ */
+ @Test
+ public void testContructorEvictionConfig() throws Exception {
+ final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
+ config.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+ config.setMinEvictableIdleTime(Duration.ofMillis(50));
+ config.setNumTestsPerEvictionRun(5);
+ try (final GenericKeyedObjectPool<String, String> p = new GenericKeyedObjectPool<>(simpleFactory, config)) {
+ for (int i = 0; i < 5; i++) {
+ p.addObject("one");
+ }
+ try {
+ Thread.sleep(100);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertEquals(5, p.getNumIdle("one"));
+ try {
+ Thread.sleep(500);
+ } catch (final InterruptedException e) {
+ // ignore
}
+ assertEquals(0, p.getNumIdle("one"));
}
}
- /*
- * 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.
+ /**
+ * Verifies that when a factory's makeObject produces instances that are not
+ * discernible by equals, the pool can handle them.
+ *
+ * JIRA: POOL-283
*/
- @SuppressWarnings({
- "rawtypes"
- })
@Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testBorrowObjectFairness() throws Exception {
+ public void testEqualsIndiscernible() throws Exception {
+ final HashSetFactory factory = new HashSetFactory();
+ try (final GenericKeyedObjectPool<String, HashSet<String>> pool = new GenericKeyedObjectPool<>(factory,
+ new GenericKeyedObjectPoolConfig<HashSet<String>>())) {
+ final HashSet<String> s1 = pool.borrowObject("a");
+ final HashSet<String> s2 = pool.borrowObject("a");
+ pool.returnObject("a", s1);
+ pool.returnObject("a", s2);
+ }
+ }
- final int numThreads = 40;
- final int maxTotal = 40;
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEviction() throws Exception {
+ gkoPool.setMaxIdlePerKey(500);
+ gkoPool.setMaxTotalPerKey(500);
+ gkoPool.setNumTestsPerEvictionRun(100);
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(250));
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
- final GenericKeyedObjectPoolConfig<String> config = new GenericKeyedObjectPoolConfig<>();
- config.setMaxTotalPerKey(maxTotal);
- config.setFairness(true);
- config.setLifo(false);
- config.setMaxIdlePerKey(maxTotal);
+ final String[] active = new String[500];
+ for(int i=0;i<500;i++) {
+ active[i] = gkoPool.borrowObject("");
+ }
+ for(int i=0;i<500;i++) {
+ gkoPool.returnObject("",active[i]);
+ }
- gkoPool = new GenericKeyedObjectPool<>(simpleFactory, config);
+ try { Thread.sleep(1000L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 300,"Should be less than 300 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 100 , "Should be less than 100 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertEquals(0,gkoPool.getNumIdle(""),"Should be zero idle, found " + gkoPool.getNumIdle(""));
- // Exhaust the pool
- final String[] objects = new String[maxTotal];
- for (int i = 0; i < maxTotal; i++) {
- objects[i] = gkoPool.borrowObject("0");
+ for(int i=0;i<500;i++) {
+ active[i] = gkoPool.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<>(gkoPool, 1, 0, 2000, false, "0" + String.valueOf(i % maxTotal), "0");
- 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());
- }
+ for(int i=0;i<500;i++) {
+ gkoPool.returnObject("",active[i]);
}
- // Return objects, other threads should get served in order
- for (int i = 0; i < maxTotal; i++) {
- gkoPool.returnObject("0", objects[i]);
+ try { Thread.sleep(1000L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 500,"Should be less than 500 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 400,"Should be less than 400 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 300,"Should be less than 300 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 200,"Should be less than 200 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle("") < 100,"Should be less than 100 idle, found " + gkoPool.getNumIdle(""));
+ try { Thread.sleep(600L); } catch(final InterruptedException e) { }
+ assertEquals(0,gkoPool.getNumIdle(""),"Should be zero idle, found " + gkoPool.getNumIdle(""));
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEviction2() throws Exception {
+ gkoPool.setMaxIdlePerKey(500);
+ gkoPool.setMaxTotalPerKey(500);
+ gkoPool.setNumTestsPerEvictionRun(100);
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(500));
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(500));
+
+ final String[] active = new String[500];
+ final String[] active2 = new String[500];
+ for (int i = 0; i < 500; i++) {
+ active[i] = gkoPool.borrowObject("");
+ active2[i] = gkoPool.borrowObject("2");
+ }
+ for (int i = 0; i < 500; i++) {
+ gkoPool.returnObject("", active[i]);
+ gkoPool.returnObject("2", active2[i]);
}
- // Wait for threads to finish
- for(int i=0;i<numThreads;i++) {
- while(!(threads[i]).complete()) {
- try {
- Thread.sleep(500L);
- } catch(final InterruptedException e) {
- // ignored
- }
- }
- if(threads[i].failed()) {
- fail("Thread "+i+" failed: "+threads[i]._exception.toString());
- }
+ try {
+ Thread.sleep(1100L);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 1000, "Should be less than 1000 idle, found " + gkoPool.getNumIdle());
+ final long sleepMillisPart2 = 600L;
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 900, "Should be less than 900 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 800, "Should be less than 800 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 700, "Should be less than 700 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 600, "Should be less than 600 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
}
+ assertTrue(gkoPool.getNumIdle() < 500, "Should be less than 500 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 400, "Should be less than 400 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 300, "Should be less than 300 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 200, "Should be less than 200 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertTrue(gkoPool.getNumIdle() < 100, "Should be less than 100 idle, found " + gkoPool.getNumIdle());
+ try {
+ Thread.sleep(sleepMillisPart2);
+ } catch (final InterruptedException e) {
+ // ignore
+ }
+ assertEquals(0, gkoPool.getNumIdle(), "Should be zero idle, found " + gkoPool.getNumIdle());
+ }
+
+ /**
+ * 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);
+ checkEvictionOrder(true);
}
- @SuppressWarnings("deprecation")
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testConstructors() throws Exception {
+ // POOL-326
+ @Test
+ public void testEvictorClearOldestRace() throws Exception {
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(100));
+ gkoPool.setNumTestsPerEvictionRun(1);
+
+ // Introduce latency between when evictor starts looking at an instance and when
+ // it decides to destroy it
+ gkoPool.setEvictionPolicy(new SlowEvictionPolicy<String>(1000));
+
+ // Borrow an instance
+ final String val = gkoPool.borrowObject("foo");
+
+ // Add another idle one
+ gkoPool.addObject("foo");
+
+ // Sleep long enough so idle one is eligible for eviction
+ Thread.sleep(1000);
+
+ // Start evictor and race with clearOldest
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(10));
- // Make constructor arguments all different from defaults
- final int maxTotalPerKey = 1;
- final int minIdle = 2;
- final long maxWait = 3;
- final int maxIdle = 4;
- final int maxTotal = 5;
- final long minEvictableIdleTimeMillis = 6;
- 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 KeyedPooledObjectFactory<Object, Object> dummyFactory = new DummyFactory();
+ // Wait for evictor to start
+ Thread.sleep(100);
+ gkoPool.clearOldest();
- try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory)) {
- assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY, objPool.getMaxTotalPerKey());
- assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY, objPool.getMaxIdlePerKey());
- assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, objPool.getMaxWaitMillis());
- assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY, objPool.getMinIdlePerKey());
- assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL, objPool.getMaxTotal());
- //
- assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
- objPool.getMinEvictableIdleTimeMillis());
- assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME,
- objPool.getMinEvictableIdleTime());
- //
- assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, objPool.getNumTestsPerEvictionRun());
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW),
- Boolean.valueOf(objPool.getTestOnBorrow()));
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN),
- Boolean.valueOf(objPool.getTestOnReturn()));
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE),
- Boolean.valueOf(objPool.getTestWhileIdle()));
- //
- assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
- objPool.getTimeBetweenEvictionRunsMillis());
- assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS,
- objPool.getTimeBetweenEvictionRuns());
- //
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED),
- Boolean.valueOf(objPool.getBlockWhenExhausted()));
- assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(objPool.getLifo()));
- }
+ // Wait for slow evictor to complete
+ Thread.sleep(1500);
- final GenericKeyedObjectPoolConfig<Object> config = new GenericKeyedObjectPoolConfig<>();
- config.setLifo(lifo);
- config.setMaxTotalPerKey(maxTotalPerKey);
- config.setMaxIdlePerKey(maxIdle);
- config.setMinIdlePerKey(minIdle);
- config.setMaxTotal(maxTotal);
- config.setMaxWaitMillis(maxWait);
- config.setMinEvictableIdleTime(Duration.ofMillis(minEvictableIdleTimeMillis));
- config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
- config.setTestOnBorrow(testOnBorrow);
- config.setTestOnReturn(testOnReturn);
- config.setTestWhileIdle(testWhileIdle);
- config.setTimeBetweenEvictionRuns(Duration.ofMillis(timeBetweenEvictionRunsMillis));
- config.setBlockWhenExhausted(blockWhenExhausted);
- try (GenericKeyedObjectPool<Object, Object> objPool = new GenericKeyedObjectPool<>(dummyFactory, config)) {
- assertEquals(maxTotalPerKey, objPool.getMaxTotalPerKey());
- assertEquals(maxIdle, objPool.getMaxIdlePerKey());
- assertEquals(maxWait, objPool.getMaxWaitMillis());
- assertEquals(minIdle, objPool.getMinIdlePerKey());
- assertEquals(maxTotal, objPool.getMaxTotal());
- assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTimeMillis());
- assertEquals(minEvictableIdleTimeMillis, objPool.getMinEvictableIdleTime().toMillis());
- assertEquals(numTestsPerEvictionRun, objPool.getNumTestsPerEvictionRun());
- assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(objPool.getTestOnBorrow()));
- assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(objPool.getTestOnReturn()));
- assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(objPool.getTestWhileIdle()));
- assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRunsMillis());
- assertEquals(timeBetweenEvictionRunsMillis, objPool.getTimeBetweenEvictionRuns().toMillis());
- assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(objPool.getBlockWhenExhausted()));
- assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(objPool.getLifo()));
- }
+ // See if we get NPE on return (POOL-326)
+ gkoPool.returnObject("foo", val);
}
+ /**
+ * Verifies that the evictor visits objects in expected order
+ * and frequency.
+ *
+ * @throws Exception May occur in some failure modes
+ */
@Test
- public void testConstructorNullFactory() {
- // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
- assertThrows(IllegalArgumentException.class,
- () -> new GenericKeyedObjectPool<>(null));
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testEvictorVisiting() throws Exception {
+ checkEvictorVisiting(true);
+ checkEvictorVisiting(false);
}
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionOnPassivateDuringReturn() throws Exception {
+ public void testExceptionOnActivateDuringBorrow() throws Exception {
+ final String obj1 = gkoPool.borrowObject("one");
+ final String obj2 = gkoPool.borrowObject("one");
+ gkoPool.returnObject("one", obj1);
+ gkoPool.returnObject("one", 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 = gkoPool.borrowObject("one");
- simpleFactory.setThrowExceptionOnPassivate(true);
+ assertEquals(1, gkoPool.getNumActive("one"));
+ assertEquals(0, gkoPool.getNumIdle("one"));
+ assertEquals(1, gkoPool.getNumActive());
+ assertEquals(0, gkoPool.getNumIdle());
+
gkoPool.returnObject("one", obj);
- assertEquals(0,gkoPool.getNumIdle());
- gkoPool.close();
+ simpleFactory.setValid(false);
+ // Validation will now fail on activation when borrowObject returns
+ // an idle instance, and then when attempting to create a new instance
+ try {
+ gkoPool.borrowObject("one");
+ fail("Expecting NoSuchElementException");
+ } catch (final NoSuchElementException ex) {
+ // expected
+ }
+ assertEquals(0, gkoPool.getNumActive("one"));
+ assertEquals(0, gkoPool.getNumIdle("one"));
+ assertEquals(0, gkoPool.getNumActive());
+ assertEquals(0, gkoPool.getNumIdle());
}
@Test
@@ -1340,187 +1544,160 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
assertEquals(0, gkoPool.getNumIdle());
}
+
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testExceptionOnActivateDuringBorrow() throws Exception {
- final String obj1 = gkoPool.borrowObject("one");
- final String obj2 = gkoPool.borrowObject("one");
- gkoPool.returnObject("one", obj1);
- gkoPool.returnObject("one", obj2);
- simpleFactory.setThrowExceptionOnActivate(true);
- simpleFactory.setEvenValid(false);
- // Activation will now throw every other time
- // First attempt throws, but loop continues and second succeeds
+ public void testExceptionOnPassivateDuringReturn() throws Exception {
final String obj = gkoPool.borrowObject("one");
- assertEquals(1, gkoPool.getNumActive("one"));
- assertEquals(0, gkoPool.getNumIdle("one"));
- assertEquals(1, gkoPool.getNumActive());
- assertEquals(0, gkoPool.getNumIdle());
-
+ simpleFactory.setThrowExceptionOnPassivate(true);
gkoPool.returnObject("one", 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
- try {
- gkoPool.borrowObject("one");
- fail("Expecting NoSuchElementException");
- } catch (final NoSuchElementException ex) {
- // expected
- }
- assertEquals(0, gkoPool.getNumActive("one"));
- assertEquals(0, gkoPool.getNumIdle("one"));
- assertEquals(0, gkoPool.getNumActive());
- assertEquals(0, gkoPool.getNumIdle());
- }
-
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testBlockedKeyDoesNotBlockPool() throws Exception {
- gkoPool.setBlockWhenExhausted(true);
- gkoPool.setMaxWaitMillis(5000);
- gkoPool.setMaxTotalPerKey(1);
- gkoPool.setMaxTotal(-1);
- gkoPool.borrowObject("one");
- 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();
- // 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
- Thread.sleep(1000);
- gkoPool.borrowObject("two");
- final long endMillis = System.currentTimeMillis();
- // If it fails it will be more than 4000ms (5000 less the 1000 sleep)
- // 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,
- "Elapsed time: " + (endMillis - startMillis) + " should be less than 4000");
-
+ assertEquals(0,gkoPool.getNumIdle());
+ gkoPool.close();
}
- private static final boolean DISPLAY_THREAD_DETAILS=
- Boolean.valueOf(System.getProperty("TestGenericKeyedObjectPool.display.thread.details", "false")).booleanValue();
- // 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
-
- /*
- * Test multi-threaded pool access.
- * Multiple keys, multiple threads, but maxActive only allows half the threads to succeed.
- *
- * This test was prompted by Continuum build failures in the Commons DBCP test case:
- * TestSharedPoolDataSource.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 = 4 * maxWait; // how long to hold connection
- final int keyCount = 4; // number of different keys
- final int threadsPerKey = 5; // number of threads to grab the key initially
- gkoPool.setBlockWhenExhausted(true);
- gkoPool.setMaxWaitMillis(maxWait);
- gkoPool.setMaxTotalPerKey(threadsPerKey);
- // Create enough threads so half the threads will have to wait
- final WaitingTestThread wtt[] = new WaitingTestThread[keyCount * threadsPerKey * 2];
- for(int i=0; i < wtt.length; i++){
- wtt[i] = new WaitingTestThread(gkoPool,Integer.toString(i % keyCount),holdTime);
- }
- final long originMillis = System.currentTimeMillis() - 1000;
- for (final WaitingTestThread element : wtt) {
- element.start();
- }
- int failed = 0;
- for (final WaitingTestThread element : wtt) {
- element.join();
- if (element._thrown != null){
- failed++;
- }
- }
- if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){
- System.out.println(
- "MaxWait: " + maxWait +
- " HoldTime: " + holdTime +
- " KeyCount: " + keyCount +
- " MaxActive: " + threadsPerKey +
- " Threads: " + wtt.length +
- " Failed: " + failed
- );
- for (final WaitingTestThread 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) +
- " Key: " + (wt._key) +
- " ObjId: " + wt.objectId
- );
- }
- }
- assertEquals(wtt.length/2,failed,"Expected half the threads to fail");
+ public void testFIFO() throws Exception {
+ gkoPool.setLifo(false);
+ final String key = "key";
+ gkoPool.addObject(key); // "key0"
+ gkoPool.addObject(key); // "key1"
+ gkoPool.addObject(key); // "key2"
+ assertEquals( "key0", gkoPool.borrowObject(key),"Oldest");
+ assertEquals( "key1", gkoPool.borrowObject(key),"Middle");
+ assertEquals( "key2", gkoPool.borrowObject(key),"Youngest");
+ final String s = gkoPool.borrowObject(key);
+ assertEquals( "key3", s,"new-3");
+ gkoPool.returnObject(key, s);
+ assertEquals( s, gkoPool.borrowObject(key),"returned");
+ assertEquals( "key4", gkoPool.borrowObject(key),"new-4");
}
/**
- * Test case for POOL-180.
+ * 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
- @Timeout(value = 200000, unit = TimeUnit.MILLISECONDS)
- public void testMaxActivePerKeyExceeded() throws Exception {
- final WaiterFactory<String> waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 8, 5, 0);
- // TODO Fix this. Can't use local pool since runTestThreads uses the
- // protected pool field
- try (final GenericKeyedObjectPool<String, Waiter> waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) {
- waiterPool.setMaxTotalPerKey(5);
- waiterPool.setMaxTotal(8);
- waiterPool.setTestOnBorrow(true);
- waiterPool.setMaxIdlePerKey(5);
- waiterPool.setMaxWaitMillis(-1);
- runTestThreads(20, 300, 250, waiterPool);
+ public void testInvalidateFreesCapacity() throws Exception {
+ final SimpleFactory<String> factory = new SimpleFactory<>();
+ try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
+ pool.setMaxTotalPerKey(2);
+ pool.setMaxWaitMillis(500);
+ // Borrow an instance and hold if for 5 seconds
+ final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000);
+ thread1.start();
+ // Borrow another instance
+ final String obj = pool.borrowObject("one");
+ // Launch another thread - will block, but fail in 500 ms
+ final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 100);
+ thread2.start();
+ // Invalidate the object borrowed by this thread - should allow thread2 to create
+ Thread.sleep(20);
+ pool.invalidateObject("one", obj);
+ Thread.sleep(600); // Wait for thread2 to timeout
+ if (thread2._thrown != null) {
+ fail(thread2._thrown.toString());
+ }
}
}
/**
- * Test to make sure that clearOldest does not destroy instances that have been checked out.
+ * Verify that threads blocked waiting on a depleted pool get served when a checked out instance
+ * is invalidated.
+ *
+ * JIRA: POOL-240
*
* @throws Exception May occur in some failure modes
*/
@Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testClearOldest() throws Exception {
- // Make destroy have some latency so clearOldest takes some time
- final WaiterFactory<String> waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 50, 5, 0);
- try (final GenericKeyedObjectPool<String, Waiter> waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) {
- waiterPool.setMaxTotalPerKey(5);
- waiterPool.setMaxTotal(50);
- waiterPool.setLifo(false);
- // Load the pool with idle instances - 5 each for 10 keys
- for (int i = 0; i < 10; i++) {
- final String key = Integer.toString(i);
- for (int j = 0; j < 5; j++) {
- waiterPool.addObject(key);
+ public void testInvalidateWaiting()
+ throws Exception {
+
+ final GenericKeyedObjectPoolConfig<Object> config = new GenericKeyedObjectPoolConfig<>();
+ config.setMaxTotal(2);
+ config.setBlockWhenExhausted(true);
+ config.setMinIdlePerKey(0);
+ config.setMaxWaitMillis(-1);
+ config.setNumTestsPerEvictionRun(Integer.MAX_VALUE); // always test all idle objects
+ config.setTestOnBorrow(true);
+ config.setTestOnReturn(false);
+ config.setTestWhileIdle(true);
+ config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
+
+ try (final GenericKeyedObjectPool<Integer, Object> pool = new GenericKeyedObjectPool<>(new ObjectFactory(),
+ config)) {
+
+ // Allocate both objects with this thread
+ pool.borrowObject(Integer.valueOf(1)); // object1
+ final Object object2 = pool.borrowObject(Integer.valueOf(1));
+
+ // Cause a thread to block waiting for an object
+ final ExecutorService executorService = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
+ final Semaphore signal = new Semaphore(0);
+ final Future<Exception> result = executorService.submit(() -> {
+ try {
+ signal.release();
+ final Object object3 = pool.borrowObject(Integer.valueOf(1));
+ pool.returnObject(Integer.valueOf(1), object3);
+ signal.release();
+ } catch (final Exception e1) {
+ return e1;
+ } catch (final Throwable e2) {
+ return new Exception(e2);
}
- // Make sure order is maintained
- Thread.sleep(20);
+
+ return null;
+ });
+
+ // Wait for the thread to start
+ assertTrue(signal.tryAcquire(5, TimeUnit.SECONDS));
+
+ // Attempt to ensure that test thread is blocked waiting for an object
+ Thread.sleep(500);
+
+ pool.invalidateObject(Integer.valueOf(1), object2);
+
+ assertTrue(signal.tryAcquire(2, TimeUnit.SECONDS),"Call to invalidateObject did not unblock pool waiters.");
+
+ if (result.get() != null) {
+ throw new AssertionError(result.get());
}
- // Now set up a race - one thread wants a new instance, triggering clearOldest
- // Other goes after an element on death row
- // See if we end up with dead man walking
- final SimpleTestThread<Waiter> t2 = new SimpleTestThread<>(waiterPool, "51");
- final Thread thread2 = new Thread(t2);
- thread2.start(); // Triggers clearOldest, killing all of the 0's and the 2 oldest 1's
- Thread.sleep(50); // Wait for clearOldest to kick off, but not long enough to reach the 1's
- final Waiter waiter = waiterPool.borrowObject("1");
- Thread.sleep(200); // Wait for execution to happen
- waiterPool.returnObject("1", waiter); // Will throw IllegalStateException if dead
}
}
+ /**
+ * Ensure the pool is registered.
+ */
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testJmxRegistration() {
+ final ObjectName oname = gkoPool.getJmxName();
+ final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ final Set<ObjectName> result = mbs.queryNames(oname, null);
+ assertEquals(1, result.size());
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testLIFO() throws Exception {
+ gkoPool.setLifo(true);
+ final String key = "key";
+ gkoPool.addObject(key); // "key0"
+ gkoPool.addObject(key); // "key1"
+ gkoPool.addObject(key); // "key2"
+ assertEquals( "key2", gkoPool.borrowObject(key),"Youngest");
+ assertEquals( "key1", gkoPool.borrowObject(key),"Middle");
+ assertEquals( "key0", gkoPool.borrowObject(key),"Oldest");
+ final String s = gkoPool.borrowObject(key);
+ assertEquals( "key3", s,"new-3");
+ gkoPool.returnObject(key, s);
+ assertEquals( s, gkoPool.borrowObject(key),"returned");
+ assertEquals( "key4", gkoPool.borrowObject(key),"new-4");
+ }
/**
* Verifies that threads that get parked waiting for keys not in use
@@ -1550,688 +1727,580 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
}
/**
- * POOL-192
- * Verify that clear(key) does not leak capacity.
+ * Verify that factory exceptions creating objects do not corrupt per key create count.
+ *
+ * JIRA: POOL-243
*
* @throws Exception May occur in some failure modes
*/
@Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testClear() throws Exception {
- gkoPool.setMaxTotal(2);
- gkoPool.setMaxTotalPerKey(2);
- gkoPool.setBlockWhenExhausted(false);
- gkoPool.addObject("one");
- gkoPool.addObject("one");
- assertEquals(2, gkoPool.getNumIdle());
- gkoPool.clear("one");
- assertEquals(0, gkoPool.getNumIdle());
- assertEquals(0, gkoPool.getNumIdle("one"));
- final String obj1 = gkoPool.borrowObject("one");
- final String obj2 = gkoPool.borrowObject("one");
- gkoPool.returnObject("one", obj1);
- gkoPool.returnObject("one", obj2);
- gkoPool.clear();
- assertEquals(0, gkoPool.getNumIdle());
- assertEquals(0, gkoPool.getNumIdle("one"));
- gkoPool.borrowObject("one");
- gkoPool.borrowObject("one");
- gkoPool.close();
+ public void testMakeObjectException() throws Exception {
+ final SimpleFactory<String> factory = new SimpleFactory<>();
+ try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
+ pool.setMaxTotalPerKey(1);
+ pool.setBlockWhenExhausted(false);
+ factory.exceptionOnCreate = true;
+ try {
+ pool.borrowObject("One");
+ } catch (final Exception ex) {
+ // expected
+ }
+ factory.exceptionOnCreate = false;
+ pool.borrowObject("One");
+ }
}
/**
- * POOL-189
+ * Test case for POOL-180.
*
* @throws Exception May occur in some failure modes
*/
@Test
+ @Timeout(value = 200000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxActivePerKeyExceeded() throws Exception {
+ final WaiterFactory<String> waiterFactory = new WaiterFactory<>(0, 20, 0, 0, 0, 0, 8, 5, 0);
+ // TODO Fix this. Can't use local pool since runTestThreads uses the
+ // protected pool field
+ try (final GenericKeyedObjectPool<String, Waiter> waiterPool = new GenericKeyedObjectPool<>(waiterFactory)) {
+ waiterPool.setMaxTotalPerKey(5);
+ waiterPool.setMaxTotal(8);
+ waiterPool.setTestOnBorrow(true);
+ waiterPool.setMaxIdlePerKey(5);
+ waiterPool.setMaxWaitMillis(-1);
+ runTestThreads(20, 300, 250, waiterPool);
+ }
+ }
+
+ @Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testWhenExhaustedBlockClosePool() throws Exception {
- gkoPool.setMaxTotalPerKey(1);
- gkoPool.setBlockWhenExhausted(true);
- gkoPool.setMaxWaitMillis(-1);
- final String obj1 = gkoPool.borrowObject("a");
+ public void testMaxIdle() throws Exception {
+ gkoPool.setMaxTotalPerKey(100);
+ gkoPool.setMaxIdlePerKey(8);
+ final String[] active = new String[100];
+ for(int i=0;i<100;i++) {
+ active[i] = gkoPool.borrowObject("");
+ }
+ assertEquals(100,gkoPool.getNumActive(""));
+ assertEquals(0,gkoPool.getNumIdle(""));
+ for(int i=0;i<100;i++) {
+ gkoPool.returnObject("",active[i]);
+ assertEquals(99 - i,gkoPool.getNumActive(""));
+ assertEquals((i < 8 ? i+1 : 8),gkoPool.getNumIdle(""));
+ }
+
+ for(int i=0;i<100;i++) {
+ active[i] = gkoPool.borrowObject("a");
+ }
+ assertEquals(100,gkoPool.getNumActive("a"));
+ assertEquals(0,gkoPool.getNumIdle("a"));
+ 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"));
+ }
+
+ // total number of idle instances is twice maxIdle
+ assertEquals(16, gkoPool.getNumIdle());
+ // Each pool is at the sup
+ assertEquals(8, gkoPool.getNumIdle(""));
+ assertEquals(8, gkoPool.getNumIdle("a"));
+
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotal() throws Exception {
+ gkoPool.setMaxTotalPerKey(2);
+ gkoPool.setMaxTotal(3);
+ gkoPool.setBlockWhenExhausted(false);
- // Make sure an object was obtained
- assertNotNull(obj1);
+ final String o1 = gkoPool.borrowObject("a");
+ assertNotNull(o1);
+ final String o2 = gkoPool.borrowObject("a");
+ assertNotNull(o2);
+ final String o3 = gkoPool.borrowObject("b");
+ assertNotNull(o3);
+ try {
+ gkoPool.borrowObject("c");
+ fail("Expected NoSuchElementException");
+ } catch(final NoSuchElementException e) {
+ // expected
+ }
- // Create a separate thread to try and borrow another object
- final WaitingTestThread wtt = new WaitingTestThread(gkoPool, "a", 200);
- wtt.start();
- // Give wtt time to start
- Thread.sleep(200);
+ assertEquals(0, gkoPool.getNumIdle());
- // close the pool (Bug POOL-189)
- gkoPool.close();
+ gkoPool.returnObject("b", o3);
+ assertEquals(1, gkoPool.getNumIdle());
+ assertEquals(1, gkoPool.getNumIdle("b"));
- // Give interrupt time to take effect
- Thread.sleep(200);
+ final Object o4 = gkoPool.borrowObject("b");
+ assertNotNull(o4);
+ assertEquals(0, gkoPool.getNumIdle());
+ assertEquals(0, gkoPool.getNumIdle("b"));
- // Check thread was interrupted
- assertTrue(wtt._thrown instanceof InterruptedException);
+ gkoPool.setMaxTotal(4);
+ final Object o5 = gkoPool.borrowObject("b");
+ assertNotNull(o5);
+
+ assertEquals(2, gkoPool.getNumActive("a"));
+ assertEquals(2, gkoPool.getNumActive("b"));
+ assertEquals(gkoPool.getMaxTotal(),
+ gkoPool.getNumActive("b") + gkoPool.getNumActive("b"));
+ assertEquals(gkoPool.getNumActive(),
+ gkoPool.getMaxTotal());
}
/**
- * POOL-231 - verify that concurrent invalidates of the same object do not
- * corrupt pool destroyCount.
+ * Verifies that maxTotal is not exceeded when factory destroyObject
+ * has high latency, testOnReturn is set and there is high incidence of
+ * validation failures.
*
* @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;
- final String key = "one";
- gkoPool.setMaxTotal(nObjects);
- gkoPool.setMaxTotalPerKey(nObjects);
- gkoPool.setMaxIdlePerKey(nObjects);
- final String [] obj = new String[nObjects];
- for (int i = 0; i < nObjects; i++) {
- obj[i] = gkoPool.borrowObject(key);
- }
- for (int i = 0; i < nObjects; i++) {
- if (i % 2 == 0) {
- gkoPool.returnObject(key, 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(gkoPool,key, 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, gkoPool.getDestroyedCount());
- }
-
- // POOL-259
- @Test
- public void testClientWaitStats() throws Exception {
- final SimpleFactory<String> factory = new SimpleFactory<>();
- // Give makeObject a little latency
- factory.setMakeLatency(200);
- try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory,
- new GenericKeyedObjectPoolConfig<String>())) {
- final String s = pool.borrowObject("one");
- // 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("one", s);
- pool.borrowObject("one");
- // 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);
- }
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalInvariant() throws Exception {
+ final int maxTotal = 15;
+ simpleFactory.setEvenValid(false); // Every other validation fails
+ simpleFactory.setDestroyLatency(100); // Destroy takes 100 ms
+ simpleFactory.setMaxTotalPerKey(maxTotal); // (makes - destroys) bound
+ simpleFactory.setValidationEnabled(true);
+ gkoPool.setMaxTotal(maxTotal);
+ gkoPool.setMaxIdlePerKey(-1);
+ gkoPool.setTestOnReturn(true);
+ gkoPool.setMaxWaitMillis(10000L);
+ runTestThreads(5, 10, 50, gkoPool);
}
- // POOL-276
@Test
- public void testValidationOnCreateOnly() throws Exception {
- simpleFactory.enableValidation = true;
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalLRU() throws Exception {
+ gkoPool.setMaxTotalPerKey(2);
+ gkoPool.setMaxTotal(3);
- gkoPool.setMaxTotal(1);
- gkoPool.setTestOnCreate(true);
- gkoPool.setTestOnBorrow(false);
- gkoPool.setTestOnReturn(false);
- gkoPool.setTestWhileIdle(false);
+ final String o1 = gkoPool.borrowObject("a");
+ assertNotNull(o1);
+ gkoPool.returnObject("a", o1);
+ Thread.sleep(25);
- final String o1 = gkoPool.borrowObject("KEY");
- assertEquals("KEY0", o1);
- final Timer t = new Timer();
- t.schedule(
- new TimerTask() {
- @Override
- public void run() {
- gkoPool.returnObject("KEY", o1);
- }
- }, 3000);
+ final String o2 = gkoPool.borrowObject("b");
+ assertNotNull(o2);
+ gkoPool.returnObject("b", o2);
+ Thread.sleep(25);
- final String o2 = gkoPool.borrowObject("KEY");
- assertEquals("KEY0", o2);
+ final String o3 = gkoPool.borrowObject("c");
+ assertNotNull(o3);
+ gkoPool.returnObject("c", o3);
+ Thread.sleep(25);
- assertEquals(1, simpleFactory.validateCounter);
- }
+ final String o4 = gkoPool.borrowObject("a");
+ assertNotNull(o4);
+ gkoPool.returnObject("a", o4);
+ Thread.sleep(25);
- /**
- * Attempts to invalidate an object, swallowing IllegalStateException.
- */
- static class InvalidateThread implements Runnable {
- private final String obj;
- private final KeyedObjectPool<String, String> pool;
- private final String key;
- private boolean done = false;
- public InvalidateThread(final KeyedObjectPool<String, String> pool, final String key, final String obj) {
- this.obj = obj;
- this.pool = pool;
- this.key = key;
- }
- @Override
- public void run() {
- try {
- pool.invalidateObject(key, obj);
- } catch (final IllegalStateException ex) {
- // Ignore
- } catch (final Exception ex) {
- fail("Unexpected exception " + ex.toString());
- } finally {
- done = true;
- }
- }
- public boolean complete() {
- return done;
- }
- }
+ assertSame(o1, o4);
- /*
- * Very simple test thread that just tries to borrow an object from
- * the provided pool with the specified key and returns it
- */
- static class SimpleTestThread<T> implements Runnable {
- private final KeyedObjectPool<String,T> _pool;
- private final String _key;
+ // this should cause b to be bumped out of the pool
+ final String o5 = gkoPool.borrowObject("d");
+ assertNotNull(o5);
+ gkoPool.returnObject("d", o5);
+ Thread.sleep(25);
- public SimpleTestThread(final KeyedObjectPool<String,T> pool, final String key) {
- _pool = pool;
- _key = key;
- }
+ // now re-request b, we should get a different object because it should
+ // have been expelled from pool (was oldest because a was requested after b)
+ final String o6 = gkoPool.borrowObject("b");
+ assertNotNull(o6);
+ gkoPool.returnObject("b", o6);
- @Override
- public void run() {
- try {
- final T obj = _pool.borrowObject(_key);
- _pool.returnObject(_key, obj);
- } catch (final Exception e) {
- // Ignore
- }
- }
- }
+ assertNotSame(o1, o6);
+ assertNotSame(o2, o6);
- /*
- * Very simple test thread that just tries to borrow an object from
- * the provided pool with the specified key and returns it after a wait
- */
- static class WaitingTestThread extends Thread {
- private final KeyedObjectPool<String,String> _pool;
- private final String _key;
- private final long _pause;
- private Throwable _thrown;
+ // second a is still in there
+ final String o7 = gkoPool.borrowObject("a");
+ assertNotNull(o7);
+ gkoPool.returnObject("a", o7);
- private long preBorrowMillis; // just before borrow
- private long postBorrowMillis; // borrow returned
- private long postReturnMillis; // after object was returned
- private long endedMillis;
- private String objectId;
+ assertSame(o4, o7);
+ }
- public WaitingTestThread(final KeyedObjectPool<String,String> pool, final String key, final long pause) {
- _pool = pool;
- _key = key;
- _pause = pause;
- _thrown = null;
- }
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalPerKey() throws Exception {
+ gkoPool.setMaxTotalPerKey(3);
+ gkoPool.setBlockWhenExhausted(false);
- @Override
- public void run() {
- try {
- preBorrowMillis = System.currentTimeMillis();
- final String obj = _pool.borrowObject(_key);
- objectId = obj;
- postBorrowMillis = System.currentTimeMillis();
- Thread.sleep(_pause);
- _pool.returnObject(_key, obj);
- postReturnMillis = System.currentTimeMillis();
- } catch (final Exception e) {
- _thrown = e;
- } finally{
- endedMillis = System.currentTimeMillis();
- }
+ gkoPool.borrowObject("");
+ gkoPool.borrowObject("");
+ gkoPool.borrowObject("");
+ try {
+ gkoPool.borrowObject("");
+ fail("Expected NoSuchElementException");
+ } catch(final NoSuchElementException e) {
+ // expected
}
}
- static class TestThread<T> implements Runnable {
- private final java.util.Random _random = new java.util.Random();
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalPerKeyZero() throws Exception {
+ gkoPool.setMaxTotalPerKey(0);
+ gkoPool.setBlockWhenExhausted(false);
- /** GKOP to hit */
- private final KeyedObjectPool<String,T> _pool;
- /** number of borrow/return iterations */
- private final int _iter;
- /** delay before borrow */
- private final int _startDelay;
- /** delay before return */
- private final int _holdTime;
- /** whether or not delays are random (with max = configured values) */
- private final boolean _randomDelay;
- /** expected object */
- private final T _expectedObject;
- /** key used in borrow / return sequence - null means random */
- private final String _key;
+ try {
+ gkoPool.borrowObject("a");
+ fail("Expected NoSuchElementException");
+ } catch(final NoSuchElementException e) {
+ // expected
+ }
+ }
- private volatile boolean _complete = false;
- private volatile boolean _failed = false;
- private volatile Exception _exception;
+ /**
+ * Verifies that if a borrow of a new key is blocked because maxTotal has
+ * been reached, that borrow continues once another object is returned.
+ *
+ * JIRA: POOL-310
+ */
+ @Test
+ public void testMaxTotalWithThreads() throws Exception {
- public TestThread(final KeyedObjectPool<String,T> pool) {
- this(pool, 100, 50, 50, true, null, null);
- }
+ gkoPool.setMaxTotalPerKey(2);
+ gkoPool.setMaxTotal(1);
- public TestThread(final KeyedObjectPool<String,T> pool, final int iter) {
- this(pool, iter, 50, 50, true, null, null);
- }
+ final int holdTime = 2000;
- public TestThread(final KeyedObjectPool<String,T> pool, final int iter, final int delay) {
- this(pool, iter, delay, delay, true, null, null);
- }
+ final TestThread<String> testA = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "a");
+ final TestThread<String> testB = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "b");
- public TestThread(final KeyedObjectPool<String,T> pool, final int iter, final int startDelay,
- final int holdTime, final boolean randomDelay, final T expectedObject, final String key) {
- _pool = pool;
- _iter = iter;
- _startDelay = startDelay;
- _holdTime = holdTime;
- _randomDelay = randomDelay;
- _expectedObject = expectedObject;
- _key = key;
+ final Thread threadA = new Thread(testA);
+ final Thread threadB = new Thread(testB);
- }
+ threadA.start();
+ threadB.start();
- public boolean complete() {
- return _complete;
- }
+ Thread.sleep(holdTime * 2);
- public boolean failed() {
- return _failed;
+ // Both threads should be complete now.
+ boolean threadRunning = true;
+ int count = 0;
+ while (threadRunning && count < 15) {
+ threadRunning = threadA.isAlive();
+ threadRunning = threadB.isAlive();
+ Thread.sleep(200);
+ count++;
}
+ assertFalse(threadA.isAlive());
+ assertFalse(threadB.isAlive());
- @Override
- public void run() {
- for(int i=0;i<_iter;i++) {
- final String key = _key == null ? String.valueOf(_random.nextInt(3)) : _key;
- try {
- Thread.sleep(_randomDelay ? _random.nextInt(_startDelay) : _startDelay);
- } catch(final InterruptedException e) {
- // ignored
- }
- T obj = null;
- try {
- obj = _pool.borrowObject(key);
- } catch(final Exception e) {
- _exception = e;
- _failed = true;
- _complete = true;
- break;
- }
+ assertFalse(testA._failed);
+ assertFalse(testB._failed);
+ }
- if (_expectedObject != null && !_expectedObject.equals(obj)) {
- _exception = new Exception("Expected: "+_expectedObject+ " found: "+obj);
- _failed = true;
- _complete = true;
- break;
- }
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMaxTotalZero() throws Exception {
+ gkoPool.setMaxTotal(0);
+ gkoPool.setBlockWhenExhausted(false);
- try {
- Thread.sleep(_randomDelay ? _random.nextInt(_holdTime) : _holdTime);
- } catch(final InterruptedException e) {
- // ignored
- }
- try {
- _pool.returnObject(key,obj);
- } catch(final Exception e) {
- _exception = e;
- _failed = true;
- _complete = true;
- break;
- }
- }
- _complete = true;
+ try {
+ gkoPool.borrowObject("a");
+ fail("Expected NoSuchElementException");
+ } catch(final NoSuchElementException e) {
+ // expected
}
}
- public static class SimpleFactory<K> implements KeyedPooledObjectFactory<K,String> {
- public SimpleFactory() {
- this(true);
- }
- public SimpleFactory(final boolean valid) {
- this.valid = valid;
- }
- @Override
- public PooledObject<String> makeObject(final K key) throws Exception {
- if (exceptionOnCreate) {
- throw new Exception();
- }
- doWait(makeLatency);
- String out = null;
- synchronized(this) {
- activeCount++;
- if (activeCount > maxTotalPerKey) {
- throw new IllegalStateException(
- "Too many active instances: " + activeCount);
- }
- out = String.valueOf(key) + String.valueOf(counter++);
- }
- return new DefaultPooledObject<>(out);
- }
- @Override
- public void destroyObject(final K key, final PooledObject<String> obj) throws Exception {
- doWait(destroyLatency);
- synchronized(this) {
- activeCount--;
- }
- if (exceptionOnDestroy) {
- throw new Exception();
- }
- }
- @Override
- public boolean validateObject(final K key, final PooledObject<String> obj) {
- doWait(validateLatency);
- if (enableValidation) {
- return validateCounter++%2 == 0 ? evenValid : oddValid;
- }
- return valid;
- }
- @Override
- public void activateObject(final K key, final PooledObject<String> obj) throws Exception {
- if (exceptionOnActivate) {
- if (!(validateCounter++%2 == 0 ? evenValid : oddValid)) {
- throw new Exception();
- }
- }
- }
- @Override
- public void passivateObject(final K key, final PooledObject<String> obj) throws Exception {
- if (exceptionOnPassivate) {
- throw new Exception();
- }
- }
- public void setMaxTotalPerKey(final int maxTotalPerKey) {
- this.maxTotalPerKey = maxTotalPerKey;
- }
- public void setDestroyLatency(final long destroyLatency) {
- this.destroyLatency = destroyLatency;
- }
- public void setMakeLatency(final long makeLatency) {
- this.makeLatency = makeLatency;
- }
- public void setValidateLatency(final long validateLatency) {
- this.validateLatency = validateLatency;
+ /*
+ * Test multi-threaded pool access.
+ * Multiple keys, multiple threads, but maxActive only allows half the threads to succeed.
+ *
+ * This test was prompted by Continuum build failures in the Commons DBCP test case:
+ * TestSharedPoolDataSource.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 = 4 * maxWait; // how long to hold connection
+ final int keyCount = 4; // number of different keys
+ final int threadsPerKey = 5; // number of threads to grab the key initially
+ gkoPool.setBlockWhenExhausted(true);
+ gkoPool.setMaxWaitMillis(maxWait);
+ gkoPool.setMaxTotalPerKey(threadsPerKey);
+ // Create enough threads so half the threads will have to wait
+ final WaitingTestThread wtt[] = new WaitingTestThread[keyCount * threadsPerKey * 2];
+ for(int i=0; i < wtt.length; i++){
+ wtt[i] = new WaitingTestThread(gkoPool,Integer.toString(i % keyCount),holdTime);
}
- public void setValidationEnabled(final boolean b) {
- enableValidation = b;
+ final long originMillis = System.currentTimeMillis() - 1000;
+ for (final WaitingTestThread element : wtt) {
+ element.start();
}
- void setEvenValid(final boolean valid) {
- evenValid = valid;
+ int failed = 0;
+ for (final WaitingTestThread element : wtt) {
+ element.join();
+ if (element._thrown != null){
+ failed++;
+ }
}
- void setValid(final boolean valid) {
- evenValid = valid;
- oddValid = valid;
+ if (DISPLAY_THREAD_DETAILS || wtt.length/2 != failed){
+ System.out.println(
+ "MaxWait: " + maxWait +
+ " HoldTime: " + holdTime +
+ " KeyCount: " + keyCount +
+ " MaxActive: " + threadsPerKey +
+ " Threads: " + wtt.length +
+ " Failed: " + failed
+ );
+ for (final WaitingTestThread 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) +
+ " Key: " + (wt._key) +
+ " ObjId: " + wt.objectId
+ );
+ }
}
+ assertEquals(wtt.length/2,failed,"Expected half the threads to fail");
+ }
- public void setThrowExceptionOnActivate(final boolean b) {
- exceptionOnActivate = b;
- }
- public void setThrowExceptionOnDestroy(final boolean b) {
- exceptionOnDestroy = b;
- }
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMinIdle() throws Exception {
+ gkoPool.setMaxIdlePerKey(500);
+ gkoPool.setMinIdlePerKey(5);
+ gkoPool.setMaxTotalPerKey(10);
+ gkoPool.setNumTestsPerEvictionRun(0);
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ gkoPool.setTestWhileIdle(true);
- public void setThrowExceptionOnPassivate(final boolean b) {
- exceptionOnPassivate = b;
- }
- volatile int counter = 0;
- final boolean valid;
+ //Generate a random key
+ final String key = "A";
- int activeCount = 0;
- int validateCounter = 0;
- boolean evenValid = true;
- boolean oddValid = true;
- boolean enableValidation = false;
- long destroyLatency = 0;
- long makeLatency = 0;
- long validateLatency = 0;
- volatile int maxTotalPerKey = Integer.MAX_VALUE;
- boolean exceptionOnPassivate = false;
- boolean exceptionOnActivate = false;
- boolean exceptionOnDestroy = false;
- boolean exceptionOnCreate = false;
+ gkoPool.preparePool(key);
- private void doWait(final long latency) {
- try {
- Thread.sleep(latency);
- } catch (final InterruptedException ex) {
- // ignore
- }
- }
- }
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
- @Override
- protected boolean isLifo() {
- return true;
- }
+ final String[] active = new String[5];
+ active[0] = gkoPool.borrowObject(key);
- @Override
- protected boolean isFifo() {
- return false;
- }
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
- private String getExceptionTrace(final Throwable t){
- final StringWriter sw = new StringWriter();
- t.printStackTrace(new PrintWriter(sw));
- return sw.toString();
- }
+ for(int i=1 ; i<5 ; i++) {
+ active[i] = gkoPool.borrowObject(key);
+ }
- private String formatSettings(final String title, final String s, final int i, final String s0, final boolean b0, final String s1, final int i1, final String s2, final int i2, final String s3, final int i3,
- final String s4, final int i4, final String s5, final int i5, final String s6, final int i6, final int zeroLength, final int oneLength, final int twoLength){
- final StringBuilder sb = new StringBuilder(80);
- sb.append(title).append(' ');
- sb.append(s).append('=').append(i).append(' ');
- sb.append(s0).append('=').append(b0).append(' ');
- sb.append(s1).append('=').append(i1).append(' ');
- sb.append(s2).append('=').append(i2).append(' ');
- sb.append(s3).append('=').append(i3).append(' ');
- sb.append(s4).append('=').append(i4).append(' ');
- sb.append(s5).append('=').append(i5).append(' ');
- sb.append(s6).append('=').append(i6).append(' ');
- sb.append("Lengths=").append(zeroLength).append(',').append(oneLength).append(',').append(twoLength).append(' ');
- return sb.toString();
- }
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
- /**
- * Ensure the pool is registered.
- */
- @Test
- @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
- public void testJmxRegistration() {
- final ObjectName oname = gkoPool.getJmxName();
- final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- final Set<ObjectName> result = mbs.queryNames(oname, null);
- assertEquals(1, result.size());
+ for(int i=0 ; i<5 ; i++) {
+ gkoPool.returnObject(key, active[i]);
+ }
+
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 10,"Should be 10 idle, found " + gkoPool.getNumIdle());
}
- /**
- * Verify that threads blocked waiting on a depleted pool get served when a checked out instance
- * is invalidated.
- *
- * JIRA: POOL-240
- *
- * @throws Exception May occur in some failure modes
- */
@Test
- public void testInvalidateWaiting()
- throws Exception {
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMinIdleMaxTotalPerKey() throws Exception {
+ gkoPool.setMaxIdlePerKey(500);
+ gkoPool.setMinIdlePerKey(5);
+ gkoPool.setMaxTotalPerKey(10);
+ gkoPool.setNumTestsPerEvictionRun(0);
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ gkoPool.setTestWhileIdle(true);
- final GenericKeyedObjectPoolConfig<Object> config = new GenericKeyedObjectPoolConfig<>();
- config.setMaxTotal(2);
- config.setBlockWhenExhausted(true);
- config.setMinIdlePerKey(0);
- config.setMaxWaitMillis(-1);
- config.setNumTestsPerEvictionRun(Integer.MAX_VALUE); // always test all idle objects
- config.setTestOnBorrow(true);
- config.setTestOnReturn(false);
- config.setTestWhileIdle(true);
- config.setTimeBetweenEvictionRuns(Duration.ofMillis(-1));
+ final String key = "A";
- try (final GenericKeyedObjectPool<Integer, Object> pool = new GenericKeyedObjectPool<>(new ObjectFactory(),
- config)) {
+ gkoPool.preparePool(key);
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " +
+ gkoPool.getNumIdle());
- // Allocate both objects with this thread
- pool.borrowObject(Integer.valueOf(1)); // object1
- final Object object2 = pool.borrowObject(Integer.valueOf(1));
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
- // Cause a thread to block waiting for an object
- final ExecutorService executorService = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
- final Semaphore signal = new Semaphore(0);
- final Future<Exception> result = executorService.submit(() -> {
- try {
- signal.release();
- final Object object3 = pool.borrowObject(Integer.valueOf(1));
- pool.returnObject(Integer.valueOf(1), object3);
- signal.release();
- } catch (final Exception e1) {
- return e1;
- } catch (final Throwable e2) {
- return new Exception(e2);
- }
+ final String[] active = new String[10];
- return null;
- });
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
- // Wait for the thread to start
- assertTrue(signal.tryAcquire(5, TimeUnit.SECONDS));
+ for(int i=0 ; i<5 ; i++) {
+ active[i] = gkoPool.borrowObject(key);
+ }
- // Attempt to ensure that test thread is blocked waiting for an object
- Thread.sleep(500);
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
- pool.invalidateObject(Integer.valueOf(1), object2);
+ for(int i=0 ; i<5 ; i++) {
+ gkoPool.returnObject(key, active[i]);
+ }
- assertTrue(signal.tryAcquire(2, TimeUnit.SECONDS),"Call to invalidateObject did not unblock pool waiters.");
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 10,"Should be 10 idle, found " + gkoPool.getNumIdle());
- if (result.get() != null) {
- throw new AssertionError(result.get());
- }
+ for(int i=0 ; i<10 ; i++) {
+ active[i] = gkoPool.borrowObject(key);
}
- }
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 0,"Should be 0 idle, found " + gkoPool.getNumIdle());
- private static class DaemonThreadFactory implements ThreadFactory {
- @Override
- public Thread newThread(final Runnable r) {
- final Thread t = new Thread(r);
- t.setDaemon(true);
- return t;
+ for(int i=0 ; i<10 ; i++) {
+ gkoPool.returnObject(key, active[i]);
}
+
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 10,"Should be 10 idle, found " + gkoPool.getNumIdle());
}
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testMinIdleNoPreparePool() throws Exception {
+ gkoPool.setMaxIdlePerKey(500);
+ gkoPool.setMinIdlePerKey(5);
+ gkoPool.setMaxTotalPerKey(10);
+ gkoPool.setNumTestsPerEvictionRun(0);
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(50));
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(100));
+ gkoPool.setTestWhileIdle(true);
+
+
+ //Generate a random key
+ final String key = "A";
+
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 0,"Should be 0 idle, found " + gkoPool.getNumIdle());
+
+ final Object active = gkoPool.borrowObject(key);
+ assertNotNull(active);
+
+ try { Thread.sleep(150L); } catch(final InterruptedException e) { }
+ assertTrue(gkoPool.getNumIdle() == 5,"Should be 5 idle, found " + gkoPool.getNumIdle());
+ }
/**
- * Verify that threads waiting on a depleted pool get served when a checked out object is
- * invalidated.
- *
- * JIRA: POOL-240
+ * Verifies that returning an object twice (without borrow in between) causes ISE
+ * but does not re-validate or re-passivate the instance.
*
- * @throws Exception May occur in some failure modes
+ * JIRA: POOL-285
*/
@Test
- public void testInvalidateFreesCapacity() throws Exception {
- final SimpleFactory<String> factory = new SimpleFactory<>();
- try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
- pool.setMaxTotalPerKey(2);
- pool.setMaxWaitMillis(500);
- // Borrow an instance and hold if for 5 seconds
- final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000);
- thread1.start();
- // Borrow another instance
- final String obj = pool.borrowObject("one");
- // Launch another thread - will block, but fail in 500 ms
- final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 100);
- thread2.start();
- // Invalidate the object borrowed by this thread - should allow thread2 to create
- Thread.sleep(20);
- pool.invalidateObject("one", obj);
- Thread.sleep(600); // Wait for thread2 to timeout
- if (thread2._thrown != null) {
- fail(thread2._thrown.toString());
+ public void testMultipleReturn() throws Exception {
+ final WaiterFactory<String> factory = new WaiterFactory<>(0, 0, 0, 0, 0, 0);
+ try (final GenericKeyedObjectPool<String, Waiter> pool = new GenericKeyedObjectPool<>(factory)) {
+ pool.setTestOnReturn(true);
+ final Waiter waiter = pool.borrowObject("a");
+ pool.returnObject("a", waiter);
+ assertEquals(1, waiter.getValidationCount());
+ assertEquals(1, waiter.getPassivationCount());
+ try {
+ pool.returnObject("a", 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());
}
}
}
/**
- * Verify that threads waiting on a depleted pool get served when a returning object fails
- * validation.
- *
- * JIRA: POOL-240
+ * 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.
*
- * @throws Exception May occur in some failure modes
+ * JIRA: POOL-284
*/
@Test
- public void testValidationFailureOnReturnFreesCapacity()
- throws Exception {
- final SimpleFactory<String> factory = new SimpleFactory<>();
- factory.setValid(false); // Validate will always fail
- factory.setValidationEnabled(true);
- try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
- pool.setMaxTotalPerKey(2);
- pool.setMaxWaitMillis(1500);
- pool.setTestOnReturn(true);
- pool.setTestOnBorrow(false);
- // Borrow an instance and hold if for 5 seconds
- final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000);
- thread1.start();
- // Borrow another instance and return it after 500 ms (validation will fail)
- final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 500);
- thread2.start();
- Thread.sleep(50);
- // Try to borrow an object
- final String obj = pool.borrowObject("one");
- pool.returnObject("one", obj);
+ public void testMutable() throws Exception {
+ final HashSetFactory factory = new HashSetFactory();
+ try (final GenericKeyedObjectPool<String, HashSet<String>> pool = new GenericKeyedObjectPool<>(factory,
+ new GenericKeyedObjectPoolConfig<HashSet<String>>())) {
+ final HashSet<String> s1 = pool.borrowObject("a");
+ final HashSet<String> s2 = pool.borrowObject("a");
+ s1.add("One");
+ s2.add("One");
+ pool.returnObject("a", s1);
+ pool.returnObject("a", s2);
}
}
- /**
- * Verify that factory exceptions creating objects do not corrupt per key create count.
- *
- * JIRA: POOL-243
- *
- * @throws Exception May occur in some failure modes
- */
@Test
- public void testMakeObjectException() throws Exception {
- final SimpleFactory<String> factory = new SimpleFactory<>();
- try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
- pool.setMaxTotalPerKey(1);
- pool.setBlockWhenExhausted(false);
- factory.exceptionOnCreate = true;
- try {
- pool.borrowObject("One");
- } catch (final Exception ex) {
- // expected
- }
- factory.exceptionOnCreate = false;
- pool.borrowObject("One");
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testNegativeMaxTotalPerKey() throws Exception {
+ gkoPool.setMaxTotalPerKey(-1);
+ gkoPool.setBlockWhenExhausted(false);
+ final String obj = gkoPool.borrowObject("");
+ assertEquals("0",obj);
+ gkoPool.returnObject("",obj);
+ }
+
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testNumActiveNumIdle2() throws Exception {
+ assertEquals(0,gkoPool.getNumActive());
+ assertEquals(0,gkoPool.getNumIdle());
+ assertEquals(0,gkoPool.getNumActive("A"));
+ assertEquals(0,gkoPool.getNumIdle("A"));
+ assertEquals(0,gkoPool.getNumActive("B"));
+ assertEquals(0,gkoPool.getNumIdle("B"));
+
+ final String objA0 = gkoPool.borrowObject("A");
+ final String objB0 = gkoPool.borrowObject("B");
+
+ assertEquals(2,gkoPool.getNumActive());
+ assertEquals(0,gkoPool.getNumIdle());
+ assertEquals(1,gkoPool.getNumActive("A"));
+ assertEquals(0,gkoPool.getNumIdle("A"));
+ assertEquals(1,gkoPool.getNumActive("B"));
+ assertEquals(0,gkoPool.getNumIdle("B"));
+
+ final String objA1 = gkoPool.borrowObject("A");
+ final String objB1 = gkoPool.borrowObject("B");
+
+ assertEquals(4,gkoPool.getNumActive());
+ assertEquals(0,gkoPool.getNumIdle());
+ assertEquals(2,gkoPool.getNumActive("A"));
+ assertEquals(0,gkoPool.getNumIdle("A"));
+ assertEquals(2,gkoPool.getNumActive("B"));
+ assertEquals(0,gkoPool.getNumIdle("B"));
+
+ gkoPool.returnObject("A",objA0);
+ gkoPool.returnObject("B",objB0);
+
+ assertEquals(2,gkoPool.getNumActive());
+ assertEquals(2,gkoPool.getNumIdle());
+ assertEquals(1,gkoPool.getNumActive("A"));
+ assertEquals(1,gkoPool.getNumIdle("A"));
+ assertEquals(1,gkoPool.getNumActive("B"));
+ assertEquals(1,gkoPool.getNumIdle("B"));
+
+ gkoPool.returnObject("A",objA1);
+ gkoPool.returnObject("B",objB1);
+
+ assertEquals(0,gkoPool.getNumActive());
+ assertEquals(4,gkoPool.getNumIdle());
+ assertEquals(0,gkoPool.getNumActive("A"));
+ assertEquals(2,gkoPool.getNumIdle("A"));
+ assertEquals(0,gkoPool.getNumActive("B"));
+ assertEquals(2,gkoPool.getNumIdle("B"));
+ }
+
+ @Test
+ public void testReturnObjectThrowsIllegalStateException() {
+ try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(new SimpleFactory<String>())) {
+ assertThrows(IllegalStateException.class,
+ () -> pool.returnObject("Foo", "Bar"));
}
}
@@ -2273,110 +2342,76 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
}
}
- /**
- * 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 GenericKeyedObjectPool<String, HashSet<String>> pool = new GenericKeyedObjectPool<>(factory,
- new GenericKeyedObjectPoolConfig<HashSet<String>>())) {
- final HashSet<String> s1 = pool.borrowObject("a");
- final HashSet<String> s2 = pool.borrowObject("a");
- pool.returnObject("a", s1);
- pool.returnObject("a", s2);
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testSettersAndGetters() throws Exception {
+ {
+ gkoPool.setMaxTotalPerKey(123);
+ assertEquals(123, gkoPool.getMaxTotalPerKey());
}
- }
-
- /**
- * 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 GenericKeyedObjectPool<String, HashSet<String>> pool = new GenericKeyedObjectPool<>(factory,
- new GenericKeyedObjectPoolConfig<HashSet<String>>())) {
- final HashSet<String> s1 = pool.borrowObject("a");
- final HashSet<String> s2 = pool.borrowObject("a");
- s1.add("One");
- s2.add("One");
- pool.returnObject("a", s1);
- pool.returnObject("a", s2);
+ {
+ gkoPool.setMaxIdlePerKey(12);
+ assertEquals(12, gkoPool.getMaxIdlePerKey());
+ }
+ {
+ gkoPool.setMaxWaitMillis(1234L);
+ assertEquals(1234L, gkoPool.getMaxWaitMillis());
+ }
+ {
+ gkoPool.setMinEvictableIdleTimeMillis(12345L);
+ assertEquals(12345L, gkoPool.getMinEvictableIdleTimeMillis());
+ }
+ {
+ gkoPool.setMinEvictableIdleTime(Duration.ofMillis(12345L));
+ assertEquals(12345L, gkoPool.getMinEvictableIdleTime().toMillis());
+ }
+ {
+ gkoPool.setNumTestsPerEvictionRun(11);
+ assertEquals(11, gkoPool.getNumTestsPerEvictionRun());
+ }
+ {
+ gkoPool.setTestOnBorrow(true);
+ assertTrue(gkoPool.getTestOnBorrow());
+ gkoPool.setTestOnBorrow(false);
+ assertTrue(!gkoPool.getTestOnBorrow());
+ }
+ {
+ gkoPool.setTestOnReturn(true);
+ assertTrue(gkoPool.getTestOnReturn());
+ gkoPool.setTestOnReturn(false);
+ assertTrue(!gkoPool.getTestOnReturn());
+ }
+ {
+ gkoPool.setTestWhileIdle(true);
+ assertTrue(gkoPool.getTestWhileIdle());
+ gkoPool.setTestWhileIdle(false);
+ assertTrue(!gkoPool.getTestWhileIdle());
+ }
+ {
+ gkoPool.setTimeBetweenEvictionRunsMillis(11235L);
+ assertEquals(11235L, gkoPool.getTimeBetweenEvictionRunsMillis());
+ }
+ {
+ gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(11235L));
+ assertEquals(11235L, gkoPool.getTimeBetweenEvictionRuns().toMillis());
+ }
+ {
+ gkoPool.setBlockWhenExhausted(true);
+ assertTrue(gkoPool.getBlockWhenExhausted());
+ gkoPool.setBlockWhenExhausted(false);
+ assertFalse(gkoPool.getBlockWhenExhausted());
}
}
- /**
- * 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 GenericKeyedObjectPool<String, Waiter> pool = new GenericKeyedObjectPool<>(factory)) {
- pool.setTestOnReturn(true);
- final Waiter waiter = pool.borrowObject("a");
- pool.returnObject("a", waiter);
- assertEquals(1, waiter.getValidationCount());
- assertEquals(1, waiter.getPassivationCount());
- try {
- pool.returnObject("a", 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());
- }
- }
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testThreaded1() throws Exception {
+ gkoPool.setMaxTotalPerKey(15);
+ gkoPool.setMaxIdlePerKey(15);
+ gkoPool.setMaxWaitMillis(1000L);
+ runTestThreads(20, 100, 50, gkoPool);
}
- /**
- * Verifies that if a borrow of a new key is blocked because maxTotal has
- * been reached, that borrow continues once another object is returned.
- *
- * JIRA: POOL-310
- */
- @Test
- public void testMaxTotalWithThreads() throws Exception {
-
- gkoPool.setMaxTotalPerKey(2);
- gkoPool.setMaxTotal(1);
-
- final int holdTime = 2000;
-
- final TestThread<String> testA = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "a");
- final TestThread<String> testB = new TestThread<>(gkoPool, 1, 0, holdTime, false, null, "b");
-
- final Thread threadA = new Thread(testA);
- final Thread threadB = new Thread(testB);
-
- threadA.start();
- threadB.start();
-
- Thread.sleep(holdTime * 2);
-
- // Both threads should be complete now.
- boolean threadRunning = true;
- int count = 0;
- while (threadRunning && count < 15) {
- threadRunning = threadA.isAlive();
- threadRunning = threadB.isAlive();
- Thread.sleep(200);
- count++;
- }
- assertFalse(threadA.isAlive());
- assertFalse(threadB.isAlive());
-
- assertFalse(testA._failed);
- assertFalse(testB._failed);
- }
// Pool-361
@Test
@@ -2408,131 +2443,96 @@ public class TestGenericKeyedObjectPool extends TestKeyedObjectPool {
assertEquals(1, gkoPool.getNumActive());
}
- // POOL-326
+ /**
+ * 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 testEvictorClearOldestRace() throws Exception {
- gkoPool.setMinEvictableIdleTime(Duration.ofMillis(100));
- gkoPool.setNumTestsPerEvictionRun(1);
-
- // Introduce latency between when evictor starts looking at an instance and when
- // it decides to destroy it
- gkoPool.setEvictionPolicy(new SlowEvictionPolicy<String>(1000));
-
- // Borrow an instance
- final String val = gkoPool.borrowObject("foo");
-
- // Add another idle one
- gkoPool.addObject("foo");
-
- // Sleep long enough so idle one is eligible for eviction
- Thread.sleep(1000);
-
- // Start evictor and race with clearOldest
- gkoPool.setTimeBetweenEvictionRuns(Duration.ofMillis(10));
-
- // Wait for evictor to start
- Thread.sleep(100);
- gkoPool.clearOldest();
-
- // Wait for slow evictor to complete
- Thread.sleep(1500);
-
- // See if we get NPE on return (POOL-326)
- gkoPool.returnObject("foo", val);
+ public void testValidationFailureOnReturnFreesCapacity()
+ throws Exception {
+ final SimpleFactory<String> factory = new SimpleFactory<>();
+ factory.setValid(false); // Validate will always fail
+ factory.setValidationEnabled(true);
+ try (final GenericKeyedObjectPool<String, String> pool = new GenericKeyedObjectPool<>(factory)) {
+ pool.setMaxTotalPerKey(2);
+ pool.setMaxWaitMillis(1500);
+ pool.setTestOnReturn(true);
+ pool.setTestOnBorrow(false);
+ // Borrow an instance and hold if for 5 seconds
+ final WaitingTestThread thread1 = new WaitingTestThread(pool, "one", 5000);
+ thread1.start();
+ // Borrow another instance and return it after 500 ms (validation will fail)
+ final WaitingTestThread thread2 = new WaitingTestThread(pool, "one", 500);
+ thread2.start();
+ Thread.sleep(50);
+ // Try to borrow an object
+ final String obj = pool.borrowObject("one");
+ pool.returnObject("one", obj);
+ }
}
+ // POOL-276
+ @Test
+ public void testValidationOnCreateOnly() throws Exception {
+ simpleFactory.enableValidation = true;
- /*
- * DefaultEvictionPolicy modified to add latency
- */
- private static class SlowEvictionPolicy<T> extends DefaultEvictionPolicy<T> {
- private final long delay;
+ gkoPool.setMaxTotal(1);
+ gkoPool.setTestOnCreate(true);
+ gkoPool.setTestOnBorrow(false);
+ gkoPool.setTestOnReturn(false);
+ gkoPool.setTestWhileIdle(false);
- /**
- * Create SlowEvictionPolicy with the given delay in ms
- *
- * @param delay number of ms of latency to inject in evict
- */
- public SlowEvictionPolicy(final long delay) {
- this.delay = delay;
- }
+ final String o1 = gkoPool.borrowObject("KEY");
+ assertEquals("KEY0", o1);
+ final Timer t = new Timer();
+ t.schedule(
+ new TimerTask() {
+ @Override
+ public void run() {
+ gkoPool.returnObject("KEY", o1);
+ }
+ }, 3000);
- @Override
- public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
- final int idleCount) {
- try {
- Thread.sleep(delay);
- } catch (final InterruptedException e) {
- // ignore
- }
- return super.evict(config, underTest, idleCount);
- }
- }
+ final String o2 = gkoPool.borrowObject("KEY");
+ assertEquals("KEY0", o2);
- private static class DummyFactory
- extends BaseKeyedPooledObjectFactory<Object,Object> {
- @Override
- public Object create(final Object key) throws Exception {
- return null;
- }
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
- }
+ assertEquals(1, simpleFactory.validateCounter);
}
/**
- * 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.
+ * POOL-189
+ *
+ * @throws Exception May occur in some failure modes
*/
- private static final class HashSetFactory
- extends BaseKeyedPooledObjectFactory<String, HashSet<String>> {
- @Override
- public HashSet<String> create(final String key) throws Exception {
- return new HashSet<>();
- }
- @Override
- public PooledObject<HashSet<String>> wrap(final HashSet<String> value) {
- return new DefaultPooledObject<>(value);
- }
- }
+ @Test
+ @Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
+ public void testWhenExhaustedBlockClosePool() throws Exception {
+ gkoPool.setMaxTotalPerKey(1);
+ gkoPool.setBlockWhenExhausted(true);
+ gkoPool.setMaxWaitMillis(-1);
+ final String obj1 = gkoPool.borrowObject("a");
- private static class SimplePerKeyFactory
- extends BaseKeyedPooledObjectFactory<Object,Object> {
- final ConcurrentHashMap<Object,AtomicInteger> map =
- new ConcurrentHashMap<>();
- @Override
- public Object create(final Object key) throws Exception {
- int counter = 0;
- final AtomicInteger Counter = map.get(key);
- if(null != Counter) {
- counter = Counter.incrementAndGet();
- } else {
- map.put(key, new AtomicInteger(0));
- counter = 0;
- }
- return String.valueOf(key) + String.valueOf(counter);
- }
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
- }
- }
+ // Make sure an object was obtained
+ assertNotNull(obj1);
- private static class ObjectFactory
- extends BaseKeyedPooledObjectFactory<Integer, Object> {
+ // Create a separate thread to try and borrow another object
+ final WaitingTestThread wtt = new WaitingTestThread(gkoPool, "a", 200);
+ wtt.start();
+ // Give wtt time to start
+ Thread.sleep(200);
- @Override
- public Object create(final Integer key)
- throws Exception {
- return new Object();
- }
+ // close the pool (Bug POOL-189)
+ gkoPool.close();
- @Override
- public PooledObject<Object> wrap(final Object value) {
- return new DefaultPooledObject<>(value);
- }
+ // Give interrupt time to take effect
+ Thread.sleep(200);
+
+ // Check thread was interrupted
+ assertTrue(wtt._thrown instanceof InterruptedException);
}
}
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 747faa2..573ed89 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
@@ -952,6 +952,29 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
assertNull(evictorExecutorField.get(null));
}
+ /**
+ * Check that a pool that starts an evictor, but is never closed does not
+ * leave EvictionTimer executor running. Confirmation check is in teardown.
+ */
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testAbandonedPool() throws Exception {
+ final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
+ config.setJmxEnabled(false);
+ GenericObjectPool<String> 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> 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 {
@@ -967,7 +990,6 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
assertEquals( 0, genericObjectPool.getNumActive(),"should be zero active");
}
-
/*
* 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
@@ -1207,6 +1229,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
assertEquals(nIterations, genericObjectPool.getDestroyedCount());
}
+
@Test
public void testConstructorNullFactory() {
// add dummy assert (won't be invoked because of IAE) to avoid "unused" warning
@@ -1214,7 +1237,6 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
() -> new GenericObjectPool<>(null));
}
-
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testConstructors() throws Exception {
@@ -2085,28 +2107,6 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
}
}
- /**
- * 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 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);
- }
-
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testMaxTotal() throws Exception {
@@ -2480,6 +2480,28 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
}
}
+ /**
+ * 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 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);
@@ -2780,6 +2802,39 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
}
/**
+ * 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.
*
@@ -2836,39 +2891,6 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
assertEquals(1, simpleFactory.validateCounter);
}
- /**
- * 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());
- }
-
@Test
@Timeout(value = 60000, unit = TimeUnit.MILLISECONDS)
public void testWhenExhaustedBlock() throws Exception {
@@ -2979,26 +3001,4 @@ public class TestGenericObjectPool extends TestBaseObjectPool {
genericObjectPool.close();
}
- /**
- * Check that a pool that starts an evictor, but is never closed does not
- * leave EvictionTimer executor running. Confirmation check is in teardown.
- */
- @SuppressWarnings("deprecation")
- @Test
- public void testAbandonedPool() throws Exception {
- final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
- config.setJmxEnabled(false);
- GenericObjectPool<String> 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> ref = new WeakReference<>(abandoned);
- abandoned = null;
- while (ref.get() != null) {
- System.gc();
- Thread.sleep(100);
- }
- }
-
}
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
index b00d8d9..ad26ced 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
@@ -29,6 +29,48 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestGenericObjectPoolClassLoaders {
+ private static class CustomClassLoader extends URLClassLoader {
+ private final int n;
+
+ CustomClassLoader(final int n) {
+ super(new URL[] { BASE_URL });
+ this.n = n;
+ }
+
+ @Override
+ public URL findResource(final String name) {
+ if (!name.endsWith(String.valueOf(n))) {
+ return null;
+ }
+
+ return super.findResource(name);
+ }
+ }
+
+ private static class CustomClassLoaderObjectFactory extends
+ BasePooledObjectFactory<URL> {
+ private final int n;
+
+ CustomClassLoaderObjectFactory(final int n) {
+ this.n = n;
+ }
+
+ @Override
+ public URL create() throws Exception {
+ final URL url = Thread.currentThread().getContextClassLoader()
+ .getResource("test" + n);
+ if (url == null) {
+ throw new IllegalStateException("Object should not be null");
+ }
+ return url;
+ }
+
+ @Override
+ public PooledObject<URL> wrap(final URL value) {
+ return new DefaultPooledObject<>(value);
+ }
+ }
+
private static final URL BASE_URL =
TestGenericObjectPoolClassLoaders.class.getResource(
"/org/apache/commons/pool2/impl/");
@@ -82,46 +124,4 @@ public class TestGenericObjectPoolClassLoaders {
Thread.currentThread().setContextClassLoader(savedClassloader);
}
}
-
- private static class CustomClassLoaderObjectFactory extends
- BasePooledObjectFactory<URL> {
- private final int n;
-
- CustomClassLoaderObjectFactory(final int n) {
- this.n = n;
- }
-
- @Override
- public URL create() throws Exception {
- final URL url = Thread.currentThread().getContextClassLoader()
- .getResource("test" + n);
- if (url == null) {
- throw new IllegalStateException("Object should not be null");
- }
- return url;
- }
-
- @Override
- public PooledObject<URL> wrap(final URL value) {
- return new DefaultPooledObject<>(value);
- }
- }
-
- private static class CustomClassLoader extends URLClassLoader {
- private final int n;
-
- CustomClassLoader(final int n) {
- super(new URL[] { BASE_URL });
- this.n = n;
- }
-
- @Override
- public URL findResource(final String name) {
- if (!name.endsWith(String.valueOf(n))) {
- return null;
- }
-
- return super.findResource(name);
- }
- }
}
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java b/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java
index edcb360..8aac00f 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestLinkedBlockingDeque.java
@@ -54,22 +54,17 @@ public class TestLinkedBlockingDeque {
}
@Test
- public void testConstructors() {
- LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
- assertEquals(Integer.MAX_VALUE, deque.remainingCapacity());
-
- deque = new LinkedBlockingDeque<>(2);
- assertEquals(2, deque.remainingCapacity());
-
- deque = new LinkedBlockingDeque<>(Arrays.asList(ONE, TWO));
- assertEquals(2, deque.size());
-
+ public void testAdd() {
+ assertTrue(deque.add(ONE));
+ assertTrue(deque.add(TWO));
try {
- deque = new LinkedBlockingDeque<>(Arrays.asList(ONE, null));
+ assertTrue(deque.add(THREE));
fail("Not supposed to get here");
- } catch (final NullPointerException npe) {
- // OK
- }
+ } catch (final IllegalStateException e) {}
+ try {
+ assertTrue(deque.add(null));
+ fail("Not supposed to get here");
+ } catch (final NullPointerException e) {}
}
@Test
@@ -97,147 +92,84 @@ public class TestLinkedBlockingDeque {
}
@Test
- public void testOfferFirst() {
- deque.offerFirst(ONE);
- deque.offerFirst(TWO);
- assertEquals(2, deque.size());
- try {
- deque.offerFirst(null);
- fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
- assertEquals(Integer.valueOf(2), deque.pop());
+ public void testClear() {
+ deque.add(ONE);
+ deque.add(TWO);
+ deque.clear();
+ deque.add(ONE);
+ assertEquals(1, deque.size());
}
@Test
- public void testOfferLast() {
- deque.offerLast(ONE);
- deque.offerLast(TWO);
- assertEquals(2, deque.size());
- try {
- deque.offerLast(null);
- fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
- assertEquals(Integer.valueOf(1), deque.pop());
- }
+ public void testConstructors() {
+ LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
+ assertEquals(Integer.MAX_VALUE, deque.remainingCapacity());
- @Test
- public void testPutFirst() throws InterruptedException {
- try {
- deque.putFirst(null);
- fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
- deque.putFirst(ONE);
- deque.putFirst(TWO);
- assertEquals(2, deque.size());
- assertEquals(Integer.valueOf(2), deque.pop());
- }
+ deque = new LinkedBlockingDeque<>(2);
+ assertEquals(2, deque.remainingCapacity());
- @Test
- public void testPutLast() throws InterruptedException {
- try {
- deque.putLast(null);
- fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
- deque.putLast(ONE);
- deque.putLast(TWO);
+ deque = new LinkedBlockingDeque<>(Arrays.asList(ONE, TWO));
assertEquals(2, deque.size());
- assertEquals(Integer.valueOf(1), deque.pop());
- }
-
- @Test
- public void testOfferFirstWithTimeout() throws InterruptedException {
- try {
- deque.offerFirst(null);
- fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
- assertTrue(deque.offerFirst(ONE, TIMEOUT_50_MILLIS));
- assertTrue(deque.offerFirst(TWO, TIMEOUT_50_MILLIS));
- assertFalse(deque.offerFirst(THREE, TIMEOUT_50_MILLIS));
- }
- @Test
- public void testOfferLastWithTimeout() throws InterruptedException {
try {
- deque.offerLast(null);
+ deque = new LinkedBlockingDeque<>(Arrays.asList(ONE, null));
fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
- assertTrue(deque.offerLast(ONE, TIMEOUT_50_MILLIS));
- assertTrue(deque.offerLast(TWO, TIMEOUT_50_MILLIS));
- assertFalse(deque.offerLast(THREE, TIMEOUT_50_MILLIS));
+ } catch (final NullPointerException npe) {
+ // OK
+ }
}
@Test
- public void testRemoveFirst() {
- try {
- deque.removeFirst();
- fail("Not supposed to get here");
- } catch (final NoSuchElementException e) {}
+ public void testContains() {
deque.add(ONE);
+ assertTrue(deque.contains(ONE));
+ assertFalse(deque.contains(TWO));
+ assertFalse(deque.contains(null));
deque.add(TWO);
- assertEquals(Integer.valueOf(1), deque.removeFirst());
- try {
- deque.removeFirst();
- deque.removeFirst();
- fail("Not supposed to get here");
- } catch (final NoSuchElementException e) {}
+ assertTrue(deque.contains(TWO));
+ assertFalse(deque.contains(THREE));
}
@Test
- public void testRemoveLast() {
+ public void testDescendingIterator() {
try {
- deque.removeLast();
+ deque.descendingIterator().next();
fail("Not supposed to get here");
} catch (final NoSuchElementException e) {}
deque.add(ONE);
deque.add(TWO);
- assertEquals(Integer.valueOf(2), deque.removeLast());
- try {
- deque.removeLast();
- deque.removeLast();
- fail("Not supposed to get here");
- } catch (final NoSuchElementException e) {}
- }
-
- @Test
- public void testPollFirst() {
- assertNull(deque.pollFirst());
- assertTrue(deque.offerFirst(ONE));
- assertTrue(deque.offerFirst(TWO));
- assertEquals(Integer.valueOf(2), deque.pollFirst());
- }
-
- @Test
- public void testPollLast() {
- assertNull(deque.pollLast());
- assertTrue(deque.offerFirst(ONE));
- assertTrue(deque.offerFirst(TWO));
- assertEquals(Integer.valueOf(1), deque.pollLast());
- }
-
- @Test
- public void testTakeFirst() throws InterruptedException {
- assertTrue(deque.offerFirst(ONE));
- assertTrue(deque.offerFirst(TWO));
- assertEquals(Integer.valueOf(2), deque.takeFirst());
+ final Iterator<Integer> iter = deque.descendingIterator();
+ assertEquals(Integer.valueOf(2), iter.next());
+ iter.remove();
+ assertEquals(Integer.valueOf(1), iter.next());
}
@Test
- public void testTakeLast() throws InterruptedException {
- assertTrue(deque.offerFirst(ONE));
- assertTrue(deque.offerFirst(TWO));
- assertEquals(Integer.valueOf(1), deque.takeLast());
- }
+ public void testDrainTo() {
+ Collection<Integer> c = new ArrayList<>();
+ deque.add(ONE);
+ deque.add(TWO);
+ assertEquals(2, deque.drainTo(c));
+ assertEquals(2, c.size());
- @Test
- public void testPollFirstWithTimeout() throws InterruptedException {
- assertNull(deque.pollFirst());
- assertNull(deque.pollFirst(TIMEOUT_50_MILLIS));
+ c = new ArrayList<>();
+ deque.add(ONE);
+ deque.add(TWO);
+ assertEquals(1, deque.drainTo(c, 1));
+ assertEquals(1, deque.size());
+ assertEquals(1, c.size());
+ assertEquals(Integer.valueOf(1), c.iterator().next());
}
@Test
- public void testPollLastWithTimeout() throws InterruptedException {
- assertNull(deque.pollLast());
- assertNull(deque.pollLast(TIMEOUT_50_MILLIS));
+ public void testElement() {
+ try {
+ deque.element();
+ fail("Not supposed to get here");
+ } catch (final NoSuchElementException e){}
+ deque.add(ONE);
+ deque.add(TWO);
+ assertEquals(Integer.valueOf(1), deque.element());
}
@Test
@@ -263,43 +195,17 @@ public class TestLinkedBlockingDeque {
}
@Test
- public void testPeekFirst() {
- assertNull(deque.peekFirst());
- deque.add(ONE);
- deque.add(TWO);
- assertEquals(Integer.valueOf(1), deque.peekFirst());
- }
-
- @Test
- public void testPeekLast() {
- assertNull(deque.peekLast());
- deque.add(ONE);
- deque.add(TWO);
- assertEquals(Integer.valueOf(2), deque.peekLast());
- }
-
- @Test
- public void testRemoveLastOccurrence() {
- assertFalse(deque.removeLastOccurrence(null));
- assertFalse(deque.removeLastOccurrence(ONE));
- deque.add(ONE);
- deque.add(ONE);
- assertTrue(deque.removeLastOccurrence(ONE));
- assertTrue(deque.size() == 1);
- }
-
- @Test
- public void testAdd() {
- assertTrue(deque.add(ONE));
- assertTrue(deque.add(TWO));
- try {
- assertTrue(deque.add(THREE));
- fail("Not supposed to get here");
- } catch (final IllegalStateException e) {}
+ public void testIterator() {
try {
- assertTrue(deque.add(null));
+ deque.iterator().next();
fail("Not supposed to get here");
- } catch (final NullPointerException e) {}
+ } catch (final NoSuchElementException e) {}
+ deque.add(ONE);
+ deque.add(TWO);
+ final Iterator<Integer> iter = deque.iterator();
+ assertEquals(Integer.valueOf(1), iter.next());
+ iter.remove();
+ assertEquals(Integer.valueOf(2), iter.next());
}
@Test
@@ -314,59 +220,60 @@ public class TestLinkedBlockingDeque {
}
@Test
- public void testPut() throws InterruptedException {
+ public void testOfferFirst() {
+ deque.offerFirst(ONE);
+ deque.offerFirst(TWO);
+ assertEquals(2, deque.size());
try {
- deque.put(null);
+ deque.offerFirst(null);
fail("Not supposed to get here");
} catch (final NullPointerException e) {}
- deque.put(ONE);
- deque.put(TWO);
+ assertEquals(Integer.valueOf(2), deque.pop());
}
@Test
- public void testOfferWithTimeout() throws InterruptedException {
- assertTrue(deque.offer(ONE, TIMEOUT_50_MILLIS));
- assertTrue(deque.offer(TWO, TIMEOUT_50_MILLIS));
- assertFalse(deque.offer(THREE, TIMEOUT_50_MILLIS));
+ public void testOfferFirstWithTimeout() throws InterruptedException {
try {
- deque.offer(null, TIMEOUT_50_MILLIS);
+ deque.offerFirst(null);
fail("Not supposed to get here");
} catch (final NullPointerException e) {}
+ assertTrue(deque.offerFirst(ONE, TIMEOUT_50_MILLIS));
+ assertTrue(deque.offerFirst(TWO, TIMEOUT_50_MILLIS));
+ assertFalse(deque.offerFirst(THREE, TIMEOUT_50_MILLIS));
}
@Test
- public void testRemove() {
+ public void testOfferLast() {
+ deque.offerLast(ONE);
+ deque.offerLast(TWO);
+ assertEquals(2, deque.size());
try {
- deque.remove();
+ deque.offerLast(null);
fail("Not supposed to get here");
- } catch (final NoSuchElementException e) {}
- deque.add(ONE);
- deque.add(TWO);
- assertEquals(Integer.valueOf(1), deque.remove());
- }
-
- @Test
- public void testTake() throws InterruptedException {
- assertTrue(deque.offerFirst(ONE));
- assertTrue(deque.offerFirst(TWO));
- assertEquals(Integer.valueOf(2), deque.take());
+ } catch (final NullPointerException e) {}
+ assertEquals(Integer.valueOf(1), deque.pop());
}
@Test
- public void testPollWithTimeout() throws InterruptedException {
- assertNull(deque.poll(TIMEOUT_50_MILLIS));
- assertNull(deque.poll(TIMEOUT_50_MILLIS));
+ public void testOfferLastWithTimeout() throws InterruptedException {
+ try {
+ deque.offerLast(null);
+ fail("Not supposed to get here");
+ } catch (final NullPointerException e) {}
+ assertTrue(deque.offerLast(ONE, TIMEOUT_50_MILLIS));
+ assertTrue(deque.offerLast(TWO, TIMEOUT_50_MILLIS));
+ assertFalse(deque.offerLast(THREE, TIMEOUT_50_MILLIS));
}
@Test
- public void testElement() {
+ public void testOfferWithTimeout() throws InterruptedException {
+ assertTrue(deque.offer(ONE, TIMEOUT_50_MILLIS));
+ assertTrue(deque.offer(TWO, TIMEOUT_50_MILLIS));
+ assertFalse(deque.offer(THREE, TIMEOUT_50_MILLIS));
try {
- deque.element();
+ deque.offer(null, TIMEOUT_50_MILLIS);
fail("Not supposed to get here");
- } catch (final NoSuchElementException e){}
- deque.add(ONE);
- deque.add(TWO);
- assertEquals(Integer.valueOf(1), deque.element());
+ } catch (final NullPointerException e) {}
}
@Test
@@ -378,32 +285,53 @@ public class TestLinkedBlockingDeque {
}
@Test
- public void testDrainTo() {
- Collection<Integer> c = new ArrayList<>();
+ public void testPeekFirst() {
+ assertNull(deque.peekFirst());
deque.add(ONE);
deque.add(TWO);
- assertEquals(2, deque.drainTo(c));
- assertEquals(2, c.size());
+ assertEquals(Integer.valueOf(1), deque.peekFirst());
+ }
- c = new ArrayList<>();
+ @Test
+ public void testPeekLast() {
+ assertNull(deque.peekLast());
deque.add(ONE);
deque.add(TWO);
- assertEquals(1, deque.drainTo(c, 1));
- assertEquals(1, deque.size());
- assertEquals(1, c.size());
- assertEquals(Integer.valueOf(1), c.iterator().next());
+ assertEquals(Integer.valueOf(2), deque.peekLast());
}
@Test
- public void testPush() {
- deque.push(ONE);
- deque.push(TWO);
- assertEquals(2, deque.size());
- try {
- deque.push(THREE);
- fail("Not supposed to get here");
- } catch (final IllegalStateException e) {}
- assertEquals(Integer.valueOf(2), deque.pop());
+ public void testPollFirst() {
+ assertNull(deque.pollFirst());
+ assertTrue(deque.offerFirst(ONE));
+ assertTrue(deque.offerFirst(TWO));
+ assertEquals(Integer.valueOf(2), deque.pollFirst());
+ }
+
+ @Test
+ public void testPollFirstWithTimeout() throws InterruptedException {
+ assertNull(deque.pollFirst());
+ assertNull(deque.pollFirst(TIMEOUT_50_MILLIS));
+ }
+
+ @Test
+ public void testPollLast() {
+ assertNull(deque.pollLast());
+ assertTrue(deque.offerFirst(ONE));
+ assertTrue(deque.offerFirst(TWO));
+ assertEquals(Integer.valueOf(1), deque.pollLast());
+ }
+
+ @Test
+ public void testPollLastWithTimeout() throws InterruptedException {
+ assertNull(deque.pollLast());
+ assertNull(deque.pollLast(TIMEOUT_50_MILLIS));
+ }
+
+ @Test
+ public void testPollWithTimeout() throws InterruptedException {
+ assertNull(deque.poll(TIMEOUT_50_MILLIS));
+ assertNull(deque.poll(TIMEOUT_50_MILLIS));
}
@Test
@@ -422,95 +350,167 @@ public class TestLinkedBlockingDeque {
} catch (final NoSuchElementException e) {}
}
+ /*
+ * https://issues.apache.org/jira/browse/POOL-281
+ *
+ * Should complete almost instantly when the issue is fixed.
+ */
@Test
- public void testContains() {
- deque.add(ONE);
- assertTrue(deque.contains(ONE));
- assertFalse(deque.contains(TWO));
- assertFalse(deque.contains(null));
- deque.add(TWO);
- assertTrue(deque.contains(TWO));
- assertFalse(deque.contains(THREE));
+ @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
+ public void testPossibleBug() {
+
+ deque = new LinkedBlockingDeque<>();
+ for (int i = 0; i < 3; i++) {
+ deque.add(Integer.valueOf(i));
+ }
+
+ // This particular sequence of method calls() (there may be others)
+ // creates an internal state that triggers an infinite loop in the
+ // iterator.
+ final Iterator<Integer> iter = deque.iterator();
+ iter.next();
+
+ deque.remove(Integer.valueOf(1));
+ deque.remove(Integer.valueOf(0));
+ deque.remove(Integer.valueOf(2));
+
+ iter.next();
}
@Test
- public void testToArray() {
- deque.add(ONE);
- deque.add(TWO);
- Object[] arr = deque.toArray();
- assertEquals(Integer.valueOf(1), arr[0]);
- assertEquals(Integer.valueOf(2), arr[1]);
+ public void testPush() {
+ deque.push(ONE);
+ deque.push(TWO);
+ assertEquals(2, deque.size());
+ try {
+ deque.push(THREE);
+ fail("Not supposed to get here");
+ } catch (final IllegalStateException e) {}
+ assertEquals(Integer.valueOf(2), deque.pop());
+ }
- arr = deque.toArray(new Integer[0]);
- assertEquals(Integer.valueOf(1), arr[0]);
- assertEquals(Integer.valueOf(2), arr[1]);
+ @Test
+ public void testPut() throws InterruptedException {
+ try {
+ deque.put(null);
+ fail("Not supposed to get here");
+ } catch (final NullPointerException e) {}
+ deque.put(ONE);
+ deque.put(TWO);
+ }
- arr = deque.toArray(new Integer[deque.size()]);
- assertEquals(Integer.valueOf(1), arr[0]);
- assertEquals(Integer.valueOf(2), arr[1]);
+ @Test
+ public void testPutFirst() throws InterruptedException {
+ try {
+ deque.putFirst(null);
+ fail("Not supposed to get here");
+ } catch (final NullPointerException e) {}
+ deque.putFirst(ONE);
+ deque.putFirst(TWO);
+ assertEquals(2, deque.size());
+ assertEquals(Integer.valueOf(2), deque.pop());
}
@Test
- public void testClear() {
+ public void testPutLast() throws InterruptedException {
+ try {
+ deque.putLast(null);
+ fail("Not supposed to get here");
+ } catch (final NullPointerException e) {}
+ deque.putLast(ONE);
+ deque.putLast(TWO);
+ assertEquals(2, deque.size());
+ assertEquals(Integer.valueOf(1), deque.pop());
+ }
+
+ @Test
+ public void testRemove() {
+ try {
+ deque.remove();
+ fail("Not supposed to get here");
+ } catch (final NoSuchElementException e) {}
deque.add(ONE);
deque.add(TWO);
- deque.clear();
- deque.add(ONE);
- assertEquals(1, deque.size());
+ assertEquals(Integer.valueOf(1), deque.remove());
}
@Test
- public void testIterator() {
+ public void testRemoveFirst() {
try {
- deque.iterator().next();
+ deque.removeFirst();
fail("Not supposed to get here");
} catch (final NoSuchElementException e) {}
deque.add(ONE);
deque.add(TWO);
- final Iterator<Integer> iter = deque.iterator();
- assertEquals(Integer.valueOf(1), iter.next());
- iter.remove();
- assertEquals(Integer.valueOf(2), iter.next());
+ assertEquals(Integer.valueOf(1), deque.removeFirst());
+ try {
+ deque.removeFirst();
+ deque.removeFirst();
+ fail("Not supposed to get here");
+ } catch (final NoSuchElementException e) {}
}
@Test
- public void testDescendingIterator() {
+ public void testRemoveLast() {
try {
- deque.descendingIterator().next();
+ deque.removeLast();
fail("Not supposed to get here");
} catch (final NoSuchElementException e) {}
deque.add(ONE);
deque.add(TWO);
- final Iterator<Integer> iter = deque.descendingIterator();
- assertEquals(Integer.valueOf(2), iter.next());
- iter.remove();
- assertEquals(Integer.valueOf(1), iter.next());
+ assertEquals(Integer.valueOf(2), deque.removeLast());
+ try {
+ deque.removeLast();
+ deque.removeLast();
+ fail("Not supposed to get here");
+ } catch (final NoSuchElementException e) {}
}
- /*
- * https://issues.apache.org/jira/browse/POOL-281
- *
- * Should complete almost instantly when the issue is fixed.
- */
@Test
- @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
- public void testPossibleBug() {
+ public void testRemoveLastOccurrence() {
+ assertFalse(deque.removeLastOccurrence(null));
+ assertFalse(deque.removeLastOccurrence(ONE));
+ deque.add(ONE);
+ deque.add(ONE);
+ assertTrue(deque.removeLastOccurrence(ONE));
+ assertTrue(deque.size() == 1);
+ }
- deque = new LinkedBlockingDeque<>();
- for (int i = 0; i < 3; i++) {
- deque.add(Integer.valueOf(i));
- }
+ @Test
+ public void testTake() throws InterruptedException {
+ assertTrue(deque.offerFirst(ONE));
+ assertTrue(deque.offerFirst(TWO));
+ assertEquals(Integer.valueOf(2), deque.take());
+ }
- // This particular sequence of method calls() (there may be others)
- // creates an internal state that triggers an infinite loop in the
- // iterator.
- final Iterator<Integer> iter = deque.iterator();
- iter.next();
+ @Test
+ public void testTakeFirst() throws InterruptedException {
+ assertTrue(deque.offerFirst(ONE));
+ assertTrue(deque.offerFirst(TWO));
+ assertEquals(Integer.valueOf(2), deque.takeFirst());
+ }
- deque.remove(Integer.valueOf(1));
- deque.remove(Integer.valueOf(0));
- deque.remove(Integer.valueOf(2));
+ @Test
+ public void testTakeLast() throws InterruptedException {
+ assertTrue(deque.offerFirst(ONE));
+ assertTrue(deque.offerFirst(TWO));
+ assertEquals(Integer.valueOf(1), deque.takeLast());
+ }
- iter.next();
+ @Test
+ public void testToArray() {
+ deque.add(ONE);
+ deque.add(TWO);
+ Object[] arr = deque.toArray();
+ assertEquals(Integer.valueOf(1), arr[0]);
+ assertEquals(Integer.valueOf(2), arr[1]);
+
+ arr = deque.toArray(new Integer[0]);
+ assertEquals(Integer.valueOf(1), arr[0]);
+ assertEquals(Integer.valueOf(2), arr[1]);
+
+ arr = deque.toArray(new Integer[deque.size()]);
+ assertEquals(Integer.valueOf(1), arr[0]);
+ assertEquals(Integer.valueOf(2), arr[1]);
}
}
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestPoolImplUtils.java b/src/test/java/org/apache/commons/pool2/impl/TestPoolImplUtils.java
index c04e5e6..1022797 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestPoolImplUtils.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestPoolImplUtils.java
@@ -25,29 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestPoolImplUtils {
- @Test
- public void testFactoryTypeSimple() {
- final Class<?> result = PoolImplUtils.getFactoryType(SimpleFactory.class);
- assertEquals(String.class, result);
- }
-
- @Test
- public void testFactoryTypeNotSimple() {
- final Class<?> result = PoolImplUtils.getFactoryType(NotSimpleFactory.class);
- assertEquals(Long.class, result);
- }
-
- private static class SimpleFactory extends BasePooledObjectFactory<String> {
- @Override
- public String create() throws Exception {
- return null;
- }
- @Override
- public PooledObject<String> wrap(final String obj) {
- return null;
- }
- }
-
@SuppressWarnings("unused")
private abstract static class FactoryAB<A,B>
extends BasePooledObjectFactory<B> {
@@ -81,4 +58,27 @@ public class TestPoolImplUtils {
return null;
}
}
+
+ private static class SimpleFactory extends BasePooledObjectFactory<String> {
+ @Override
+ public String create() throws Exception {
+ return null;
+ }
+ @Override
+ public PooledObject<String> wrap(final String obj) {
+ return null;
+ }
+ }
+
+ @Test
+ public void testFactoryTypeNotSimple() {
+ final Class<?> result = PoolImplUtils.getFactoryType(NotSimpleFactory.class);
+ assertEquals(Long.class, result);
+ }
+
+ @Test
+ public void testFactoryTypeSimple() {
+ final Class<?> result = PoolImplUtils.getFactoryType(SimpleFactory.class);
+ assertEquals(String.class, result);
+ }
}
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestPooledSoftReference.java b/src/test/java/org/apache/commons/pool2/impl/TestPooledSoftReference.java
index 1681639..80d7142 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestPooledSoftReference.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestPooledSoftReference.java
@@ -30,9 +30,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
*/
public class TestPooledSoftReference {
- PooledSoftReference<String> ref;
private static final String REFERENT = "test";
private static final String REFERENT2 = "test2";
+ PooledSoftReference<String> ref;
@BeforeEach
public void setUp() {
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestSoftRefOutOfMemory.java b/src/test/java/org/apache/commons/pool2/impl/TestSoftRefOutOfMemory.java
index f249a68..d9055ab 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestSoftRefOutOfMemory.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestSoftRefOutOfMemory.java
@@ -34,6 +34,96 @@ import org.junit.jupiter.api.Test;
/**
*/
public class TestSoftRefOutOfMemory {
+ public static class LargePoolableObjectFactory extends BasePooledObjectFactory<String> {
+ private final String buffer;
+ private int counter = 0;
+
+ public LargePoolableObjectFactory(final int size) {
+ final char[] data = new char[size];
+ Arrays.fill(data, '.');
+ buffer = new String(data);
+ }
+
+ @Override
+ public String create() {
+ counter++;
... 1005 lines suppressed ...
[commons-pool] 01/04: Sort members.
Posted by gg...@apache.org.
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 a604ec2895fe37cbc67b2e330e8009dca4b5097d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Feb 15 18:01:22 2021 -0500
Sort members.
---
src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java b/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
index 90b5039..6ba43b5 100644
--- a/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
+++ b/src/main/java/org/apache/commons/pool2/impl/PoolImplUtils.java
@@ -158,6 +158,10 @@ class PoolImplUtils {
return null;
}
+ static boolean isPositive(final Duration delay) {
+ return !delay.isNegative() && !delay.isZero();
+ }
+
/**
* Converts a {@link TimeUnit} to a {@link ChronoUnit}.
*
@@ -196,8 +200,4 @@ class PoolImplUtils {
static Duration toDuration(long amount, TimeUnit timeUnit) {
return Duration.of(amount, PoolImplUtils.toChronoUnit(timeUnit));
}
-
- static boolean isPositive(final Duration delay) {
- return !delay.isNegative() && !delay.isZero();
- }
}
[commons-pool] 04/04: Add and use java.time.Duration APIs timeouts
instead of using ints for seconds.
Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git
commit a6930dbc5d88e1001364b5638c74268ebd9bdc55
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Feb 15 18:47:04 2021 -0500
Add and use java.time.Duration APIs timeouts instead of using ints for
seconds.
---
.../org/apache/commons/pool2/PooledObject.java | 29 +++++-
.../commons/pool2/impl/BaseGenericObjectPool.java | 25 +++--
.../commons/pool2/impl/GenericKeyedObjectPool.java | 5 +-
.../commons/pool2/impl/GenericObjectPool.java | 102 +++++++++++++++------
.../pool2/impl/TestBaseGenericObjectPool.java | 18 ++--
.../pool2/impl/TestDefaultPooledObject.java | 25 +++--
6 files changed, 142 insertions(+), 62 deletions(-)
diff --git a/src/main/java/org/apache/commons/pool2/PooledObject.java b/src/main/java/org/apache/commons/pool2/PooledObject.java
index 96f726d..2e46997 100644
--- a/src/main/java/org/apache/commons/pool2/PooledObject.java
+++ b/src/main/java/org/apache/commons/pool2/PooledObject.java
@@ -17,6 +17,7 @@
package org.apache.commons.pool2;
import java.io.PrintWriter;
+import java.time.Duration;
import java.util.Deque;
/**
@@ -75,7 +76,19 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
boolean equals(Object obj);
/**
- * Gets the time in milliseconds that this object last spent in the
+ * Gets the amount of time this object last spent in the
+ * active state (it may still be active in which case subsequent calls will
+ * return an increased value).
+ *
+ * @return The duration last spent in the active state
+ * @since 2.12.0
+ */
+ default Duration getActiveTime() {
+ return Duration.ofMillis(getActiveTimeMillis());
+ }
+
+ /**
+ * Gets the amount of time in milliseconds this object last spent in the
* active state (it may still be active in which case subsequent calls will
* return an increased value).
*
@@ -102,7 +115,19 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> {
long getCreateTime();
/**
- * Gets the time in milliseconds that this object last spend in the
+ * Gets the amount of time that this object last spend in the
+ * idle state (it may still be idle in which case subsequent calls will
+ * return an increased value).
+ *
+ * @return The amount of time in last spent in the idle state.
+ * @since 2.10.0
+ */
+ default Duration getIdleTime() {
+ return Duration.ofMillis(getIdleTimeMillis());
+ }
+
+ /**
+ * Gets the amount of time in milliseconds that this object last spend in the
* idle state (it may still be idle in which case subsequent calls will
* return an increased value).
*
diff --git a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
index a0d05c9..1ab282a 100644
--- a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java
@@ -248,7 +248,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
*
* @param size number of values to maintain in the cache.
*/
- public StatsStore(final int size) {
+ StatsStore(final int size) {
this.size = size;
values = new AtomicLong[size];
for (int i = 0; i < size; i++) {
@@ -256,13 +256,17 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
}
}
+ void add(Duration value) {
+ add(value.toMillis());
+ }
+
/**
* Adds a value to the cache. If the cache is full, one of the
* existing values is replaced by the new value.
*
* @param value new value to add to the cache.
*/
- public synchronized void add(final long value) {
+ synchronized void add(final long value) {
values[index].set(value);
index++;
if (index == size) {
@@ -1544,30 +1548,33 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject {
/**
* Updates statistics after an object is borrowed from the pool.
+ *
* @param p object borrowed from the pool
* @param waitTime time (in milliseconds) that the borrowing thread had to wait
*/
- final void updateStatsBorrow(final PooledObject<T> p, final long waitTime) {
+ final void updateStatsBorrow(final PooledObject<T> p, final Duration waitTime) {
borrowedCount.incrementAndGet();
- idleTimes.add(p.getIdleTimeMillis());
+ idleTimes.add(p.getIdleTime());
waitTimes.add(waitTime);
+ final long waitTimeMillis = waitTime.toMillis();
// lock-free optimistic-locking maximum
- long currentMax;
+ long currentMaxMillis;
do {
- currentMax = maxBorrowWaitTimeMillis.get();
- if (currentMax >= waitTime) {
+ currentMaxMillis = maxBorrowWaitTimeMillis.get();
+ if (currentMaxMillis >= waitTimeMillis) {
break;
}
- } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMax, waitTime));
+ } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMaxMillis, waitTimeMillis));
}
/**
* Updates statistics after an object is returned to the pool.
+ *
* @param activeTime the amount of time (in milliseconds) that the returning
* object was checked out
*/
- final void updateStatsReturn(final long activeTime) {
+ final void updateStatsReturn(final Duration activeTime) {
returnedCount.incrementAndGet();
activeTimes.add(activeTime);
}
diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
index e4098c4..50c7f2b 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.pool2.impl;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
@@ -474,7 +475,7 @@ public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T>
deregister(key);
}
- updateStatsBorrow(p, System.currentTimeMillis() - waitTimeMillis);
+ updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis));
return p.getObject();
}
@@ -1427,7 +1428,7 @@ public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T>
markReturningState(p);
- final long activeTime = p.getActiveTimeMillis();
+ final Duration activeTime = p.getActiveTime();
try {
if (getTestOnReturn() && !factory.validateObject(key, p)) {
diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
index e602a5e..97d38a2 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -16,16 +16,6 @@
*/
package org.apache.commons.pool2.impl;
-import org.apache.commons.pool2.DestroyMode;
-import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.PoolUtils;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.PooledObjectFactory;
-import org.apache.commons.pool2.PooledObjectState;
-import org.apache.commons.pool2.SwallowedExceptionListener;
-import org.apache.commons.pool2.TrackedUse;
-import org.apache.commons.pool2.UsageTracking;
-
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashSet;
@@ -34,9 +24,18 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
+import org.apache.commons.pool2.DestroyMode;
+import org.apache.commons.pool2.ObjectPool;
+import org.apache.commons.pool2.PoolUtils;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.PooledObjectState;
+import org.apache.commons.pool2.SwallowedExceptionListener;
+import org.apache.commons.pool2.TrackedUse;
+import org.apache.commons.pool2.UsageTracking;
+
/**
* A configurable {@link ObjectPool} implementation.
* <p>
@@ -272,7 +271,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
* available instances in request arrival order.
* </p>
*
- * @param borrowMaxWaitMillis The time to wait in milliseconds for an object
+ * @param borrowMaxWait The time to wait for an object
* to become available
*
* @return object instance from the pool
@@ -281,14 +280,14 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
*
* @throws Exception if an object instance cannot be returned due to an
* error
+ * @since 2.10.0
*/
- public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
+ public T borrowObject(final Duration borrowMaxWait) throws Exception {
assertOpen();
final AbandonedConfig ac = this.abandonedConfig;
- if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
- (getNumIdle() < 2) &&
- (getNumActive() > getMaxTotal() - 3) ) {
+ if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) &&
+ (getNumActive() > getMaxTotal() - 3)) {
removeAbandoned(ac);
}
@@ -312,16 +311,14 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
}
if (blockWhenExhausted) {
if (p == null) {
- if (borrowMaxWaitMillis < 0) {
+ if (borrowMaxWait.isNegative()) {
p = idleObjects.takeFirst();
} else {
- p = idleObjects.pollFirst(borrowMaxWaitMillis,
- TimeUnit.MILLISECONDS);
+ p = idleObjects.pollFirst(borrowMaxWait);
}
}
if (p == null) {
- throw new NoSuchElementException(
- "Timeout waiting for idle object");
+ throw new NoSuchElementException("Timeout waiting for idle object");
}
} else {
if (p == null) {
@@ -343,8 +340,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
}
p = null;
if (create) {
- final NoSuchElementException nsee = new NoSuchElementException(
- "Unable to activate object");
+ final NoSuchElementException nsee = new NoSuchElementException("Unable to activate object");
nsee.initCause(e);
throw nsee;
}
@@ -367,8 +363,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
}
p = null;
if (create) {
- final NoSuchElementException nsee = new NoSuchElementException(
- "Unable to validate object");
+ final NoSuchElementException nsee = new NoSuchElementException("Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
@@ -377,12 +372,65 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
}
}
- updateStatsBorrow(p, System.currentTimeMillis() - waitTimeMillis);
+ updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis));
return p.getObject();
}
/**
+ * Borrows an object from the pool using the specific waiting time which only
+ * applies if {@link #getBlockWhenExhausted()} is true.
+ * <p>
+ * If there is one or more idle instance available in the pool, then an
+ * idle instance will be selected based on the value of {@link #getLifo()},
+ * activated and returned. If activation fails, or {@link #getTestOnBorrow()
+ * testOnBorrow} is set to {@code true} and validation fails, the
+ * instance is destroyed and the next available instance is examined. This
+ * continues until either a valid instance is returned or there are no more
+ * idle instances available.
+ * </p>
+ * <p>
+ * If there are no idle instances available in the pool, behavior depends on
+ * the {@link #getMaxTotal() maxTotal}, (if applicable)
+ * {@link #getBlockWhenExhausted()} and the value passed in to the
+ * {@code borrowMaxWaitMillis} parameter. If the number of instances
+ * checked out from the pool is less than {@code maxTotal,} a new
+ * instance is created, activated and (if applicable) validated and returned
+ * to the caller. If validation fails, a {@code NoSuchElementException}
+ * is thrown.
+ * </p>
+ * <p>
+ * If the pool is exhausted (no available idle instances and no capacity to
+ * create new ones), this method will either block (if
+ * {@link #getBlockWhenExhausted()} is true) or throw a
+ * {@code NoSuchElementException} (if
+ * {@link #getBlockWhenExhausted()} is false). The length of time that this
+ * method will block when {@link #getBlockWhenExhausted()} is true is
+ * determined by the value passed in to the {@code borrowMaxWaitMillis}
+ * parameter.
+ * </p>
+ * <p>
+ * When the pool is exhausted, multiple calling threads may be
+ * simultaneously blocked waiting for instances to become available. A
+ * "fairness" algorithm has been implemented to ensure that threads receive
+ * available instances in request arrival order.
+ * </p>
+ *
+ * @param borrowMaxWaitMillis The time to wait in milliseconds for an object
+ * to become available
+ *
+ * @return object instance from the pool
+ *
+ * @throws NoSuchElementException if an instance cannot be returned
+ *
+ * @throws Exception if an object instance cannot be returned due to an
+ * error
+ */
+ public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
+ return borrowObject(Duration.ofMillis(borrowMaxWaitMillis));
+ }
+
+ /**
* Clears any objects sitting idle in the pool by removing them from the
* idle instance pool and then invoking the configured
* {@link PooledObjectFactory#destroyObject(PooledObject)} method on each
@@ -1080,7 +1128,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
markReturningState(p);
- final long activeTime = p.getActiveTimeMillis();
+ final Duration activeTime = p.getActiveTime();
if (getTestOnReturn() && !factory.validateObject(p)) {
try {
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
index 30307d2..6beb283 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java
@@ -50,7 +50,7 @@ public class TestBaseGenericObjectPool {
@Test
public void testActiveTimeStatistics() {
for (int i = 0; i < 99; i++) { // must be < MEAN_TIMING_STATS_CACHE_SIZE
- pool.updateStatsReturn(i);
+ pool.updateStatsReturn(Duration.ofMillis(i));
}
assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE);
}
@@ -58,10 +58,10 @@ public class TestBaseGenericObjectPool {
@Test
public void testBorrowWaitStatistics() {
final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject();
- pool.updateStatsBorrow(p, 10);
- pool.updateStatsBorrow(p, 20);
- pool.updateStatsBorrow(p, 20);
- pool.updateStatsBorrow(p, 30);
+ pool.updateStatsBorrow(p, Duration.ofMillis(10));
+ pool.updateStatsBorrow(p, Duration.ofMillis(20));
+ pool.updateStatsBorrow(p, Duration.ofMillis(20));
+ pool.updateStatsBorrow(p, Duration.ofMillis(30));
assertEquals(20, pool.getMeanBorrowWaitTimeMillis(), Double.MIN_VALUE);
assertEquals(30, pool.getMaxBorrowWaitTimeMillis(), 0);
}
@@ -69,13 +69,13 @@ public class TestBaseGenericObjectPool {
public void testBorrowWaitStatisticsMax() {
final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject();
assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
- pool.updateStatsBorrow(p, 0);
+ pool.updateStatsBorrow(p, Duration.ZERO);
assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
- pool.updateStatsBorrow(p, 20);
+ pool.updateStatsBorrow(p, Duration.ofMillis(20));
assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
- pool.updateStatsBorrow(p, 20);
+ pool.updateStatsBorrow(p, Duration.ofMillis(20));
assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
- pool.updateStatsBorrow(p, 10);
+ pool.updateStatsBorrow(p, Duration.ofMillis(10));
assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE);
}
diff --git a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java
index cc3f63e..ad02d5c 100644
--- a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java
+++ b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java
@@ -35,21 +35,20 @@ public class TestDefaultPooledObject {
* @throws Exception May occur in some failure modes
*/
@Test
- public void testgetIdleTimeMillis() throws Exception {
+ public void testGetIdleTimeMillis() throws Exception {
final DefaultPooledObject<Object> dpo = new DefaultPooledObject<>(new Object());
final AtomicBoolean negativeIdleTimeReturned = new AtomicBoolean(false);
- final ExecutorService executor = Executors.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors()*3);
+ final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 3);
final Runnable allocateAndDeallocateTask = () -> {
- for (int i1=0;i1<10000;i1++) {
- if (dpo.getIdleTimeMillis() < 0) {
+ for (int i1 = 0; i1 < 10000; i1++) {
+ if (dpo.getIdleTime().isNegative()) {
negativeIdleTimeReturned.set(true);
break;
}
}
dpo.allocate();
- for (int i2=0;i2<10000;i2++) {
- if (dpo.getIdleTimeMillis() < 0) {
+ for (int i2 = 0; i2 < 10000; i2++) {
+ if (dpo.getIdleTime().isNegative()) {
negativeIdleTimeReturned.set(true);
break;
}
@@ -57,8 +56,8 @@ public class TestDefaultPooledObject {
dpo.deallocate();
};
final Runnable getIdleTimeTask = () -> {
- for (int i=0;i<10000;i++) {
- if (dpo.getIdleTimeMillis() < 0) {
+ for (int i = 0; i < 10000; i++) {
+ if (dpo.getIdleTime().isNegative()) {
negativeIdleTimeReturned.set(true);
break;
}
@@ -67,15 +66,15 @@ public class TestDefaultPooledObject {
final double probabilityOfAllocationTask = 0.7;
final List<Future<?>> futures = new ArrayList<>();
for (int i = 1; i <= 10000; i++) {
- final Runnable randomTask = Math.random() < probabilityOfAllocationTask ?
- allocateAndDeallocateTask : getIdleTimeTask;
+ final Runnable randomTask = Math.random() < probabilityOfAllocationTask ? allocateAndDeallocateTask
+ : getIdleTimeTask;
futures.add(executor.submit(randomTask));
}
- for (final Future<?> future: futures) {
+ for (final Future<?> future : futures) {
future.get();
}
assertFalse(negativeIdleTimeReturned.get(),
- "DefaultPooledObject.getIdleTimeMillis() returned a negative value");
+ "DefaultPooledObject.getIdleTimeMillis() returned a negative value");
}
}
\ No newline at end of file