Repository: commons-pool Updated Branches: refs/heads/master 04d1f0d84 -> 65f5d9204
http://git-wip-us.apache.org/repos/asf/commons-pool/blob/0a542546/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java index fd5a871..adc8158 100644 --- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java +++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java @@ -60,1220 +60,1137 @@ import org.junit.Test; */ public class TestGenericObjectPool extends TestBaseObjectPool { + protected static class AtomicIntegerFactory + extends BasePooledObjectFactory<AtomicInteger> { + + private long activateLatency = 0; + private long passivateLatency = 0; + private long createLatency = 0; + private long destroyLatency = 0; + private long validateLatency = 0; + @Override - protected ObjectPool<String> makeEmptyPool(final int mincap) { - final GenericObjectPool<String> mtPool = - new GenericObjectPool<>(new SimpleFactory()); - mtPool.setMaxTotal(mincap); - mtPool.setMaxIdle(mincap); - return mtPool; + public void activateObject(final PooledObject<AtomicInteger> p) { + p.getObject().incrementAndGet(); + try { + Thread.sleep(activateLatency); + } catch (final InterruptedException ex) {} } @Override - protected ObjectPool<Object> makeEmptyPool( - final PooledObjectFactory<Object> fac) { - return new GenericObjectPool<>(fac); + public AtomicInteger create() { + try { + Thread.sleep(createLatency); + } catch (final InterruptedException ex) {} + return new AtomicInteger(0); } @Override - protected Object getNthObject(final int n) { - return String.valueOf(n); + public void destroyObject(final PooledObject<AtomicInteger> p) { + try { + Thread.sleep(destroyLatency); + } catch (final InterruptedException ex) {} } - @Before - public void setUp() throws Exception { - simpleFactory = new SimpleFactory(); - genericObjectPool = new GenericObjectPool<>(simpleFactory); + @Override + public void passivateObject(final PooledObject<AtomicInteger> p) { + p.getObject().decrementAndGet(); + try { + Thread.sleep(passivateLatency); + } catch (final InterruptedException ex) {} } - @After - public void tearDown() throws Exception { - final String poolName = genericObjectPool.getJmxName().toString(); - genericObjectPool.clear(); - genericObjectPool.close(); - genericObjectPool = null; - simpleFactory = null; - - final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - final Set<ObjectName> result = mbs.queryNames(new ObjectName( - "org.apache.commoms.pool2:type=GenericObjectPool,*"), null); - // There should be no registered pools at this point - final int registeredPoolCount = result.size(); - final StringBuilder msg = new StringBuilder("Current pool is: "); - msg.append(poolName); - msg.append(" Still open pools are: "); - for (final ObjectName name : result) { - // Clean these up ready for the next test - msg.append(name.toString()); - msg.append(" created via\n"); - msg.append(mbs.getAttribute(name, "CreationStackTrace")); - msg.append('\n'); - mbs.unregisterMBean(name); - } - Assert.assertEquals(msg.toString(), 0, registeredPoolCount); + /** + * @param activateLatency the activateLatency to set + */ + public void setActivateLatency(final long activateLatency) { + this.activateLatency = activateLatency; } - @Test(expected=IllegalArgumentException.class) - public void testConstructorNullFactory() { - // add dummy assert (won't be invoked because of IAE) to avoid "unused" warning - assertNotNull(new GenericObjectPool<String>(null)); - // TODO this currently causes tearDown to report an error - // Looks like GOP needs to call close() or jmxUnregister() before throwing IAE + /** + * @param createLatency the createLatency to set + */ + public void setCreateLatency(final long createLatency) { + this.createLatency = createLatency; } - @Test(timeout=60000) - public void testConstructors() throws Exception { - - // Make constructor arguments all different from defaults - 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 PooledObjectFactory<Object> dummyFactory = new DummyFactory(); - try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory)) { - assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_IDLE, dummyPool.getMaxIdle()); - assertEquals(BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS, dummyPool.getMaxWaitMillis()); - assertEquals(GenericObjectPoolConfig.DEFAULT_MIN_IDLE, dummyPool.getMinIdle()); - assertEquals(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL, dummyPool.getMaxTotal()); - assertEquals(BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS, - dummyPool.getMinEvictableIdleTimeMillis()); - assertEquals(BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN, - dummyPool.getNumTestsPerEvictionRun()); - assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW), - Boolean.valueOf(dummyPool.getTestOnBorrow())); - assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN), - Boolean.valueOf(dummyPool.getTestOnReturn())); - assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE), - Boolean.valueOf(dummyPool.getTestWhileIdle())); - assertEquals(BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS, - dummyPool.getTimeBetweenEvictionRunsMillis()); - assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED), - Boolean.valueOf(dummyPool.getBlockWhenExhausted())); - assertEquals(Boolean.valueOf(BaseObjectPoolConfig.DEFAULT_LIFO), Boolean.valueOf(dummyPool.getLifo())); - } - final GenericObjectPoolConfig config = new GenericObjectPoolConfig(); - config.setLifo(lifo); - config.setMaxIdle(maxIdle); - config.setMinIdle(minIdle); - config.setMaxTotal(maxTotal); - config.setMaxWaitMillis(maxWait); - config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); - config.setNumTestsPerEvictionRun(numTestsPerEvictionRun); - config.setTestOnBorrow(testOnBorrow); - config.setTestOnReturn(testOnReturn); - config.setTestWhileIdle(testWhileIdle); - config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); - config.setBlockWhenExhausted(blockWhenExhausted); - try (GenericObjectPool<Object> dummyPool = new GenericObjectPool<>(dummyFactory, config)) { - assertEquals(maxIdle, dummyPool.getMaxIdle()); - assertEquals(maxWait, dummyPool.getMaxWaitMillis()); - assertEquals(minIdle, dummyPool.getMinIdle()); - assertEquals(maxTotal, dummyPool.getMaxTotal()); - assertEquals(minEvictableIdleTimeMillis, dummyPool.getMinEvictableIdleTimeMillis()); - assertEquals(numTestsPerEvictionRun, dummyPool.getNumTestsPerEvictionRun()); - assertEquals(Boolean.valueOf(testOnBorrow), Boolean.valueOf(dummyPool.getTestOnBorrow())); - assertEquals(Boolean.valueOf(testOnReturn), Boolean.valueOf(dummyPool.getTestOnReturn())); - assertEquals(Boolean.valueOf(testWhileIdle), Boolean.valueOf(dummyPool.getTestWhileIdle())); - assertEquals(timeBetweenEvictionRunsMillis, dummyPool.getTimeBetweenEvictionRunsMillis()); - assertEquals(Boolean.valueOf(blockWhenExhausted), Boolean.valueOf(dummyPool.getBlockWhenExhausted())); - assertEquals(Boolean.valueOf(lifo), Boolean.valueOf(dummyPool.getLifo())); - } + /** + * @param destroyLatency the destroyLatency to set + */ + public void setDestroyLatency(final long destroyLatency) { + this.destroyLatency = destroyLatency; } - @Test(timeout=60000) - public void testWhenExhaustedFail() throws Exception { - genericObjectPool.setMaxTotal(1); - genericObjectPool.setBlockWhenExhausted(false); - final String obj1 = genericObjectPool.borrowObject(); - assertNotNull(obj1); - try { - genericObjectPool.borrowObject(); - fail("Expected NoSuchElementException"); - } catch(final NoSuchElementException e) { - // expected - } - genericObjectPool.returnObject(obj1); - assertEquals(1, genericObjectPool.getNumIdle()); - genericObjectPool.close(); - } - @Test(timeout=60000) - public void testWhenExhaustedBlock() throws Exception { - genericObjectPool.setMaxTotal(1); - genericObjectPool.setBlockWhenExhausted(true); - genericObjectPool.setMaxWaitMillis(10L); - final String obj1 = genericObjectPool.borrowObject(); - assertNotNull(obj1); - try { - genericObjectPool.borrowObject(); - fail("Expected NoSuchElementException"); - } catch(final NoSuchElementException e) { - // expected - } - genericObjectPool.returnObject(obj1); - genericObjectPool.close(); + /** + * @param passivateLatency the passivateLatency to set + */ + public void setPassivateLatency(final long passivateLatency) { + this.passivateLatency = passivateLatency; } - @Test(timeout=60000) - public void testWhenExhaustedBlockInterupt() throws Exception { - genericObjectPool.setMaxTotal(1); - genericObjectPool.setBlockWhenExhausted(true); - genericObjectPool.setMaxWaitMillis(-1); - final String obj1 = genericObjectPool.borrowObject(); - // Make sure on object was obtained - assertNotNull(obj1); + /** + * @param validateLatency the validateLatency to set + */ + public void setValidateLatency(final long validateLatency) { + this.validateLatency = validateLatency; + } - // Create a separate thread to try and borrow another object - final WaitingTestThread wtt = new WaitingTestThread(genericObjectPool, 200000); - wtt.start(); - // Give wtt time to start - Thread.sleep(200); - wtt.interrupt(); - // Give interrupt time to take effect - Thread.sleep(200); + @Override + public boolean validateObject(final PooledObject<AtomicInteger> instance) { + try { + Thread.sleep(validateLatency); + } catch (final InterruptedException ex) {} + return instance.getObject().intValue() == 1; + } - // Check thread was interrupted - assertTrue(wtt._thrown instanceof InterruptedException); - // Return object to the pool - genericObjectPool.returnObject(obj1); + @Override + public PooledObject<AtomicInteger> wrap(final AtomicInteger integer) { + return new DefaultPooledObject<>(integer); + } +} - // Bug POOL-162 - check there is now an object in the pool - genericObjectPool.setMaxWaitMillis(10L); - String obj2 = null; - try { - obj2 = genericObjectPool.borrowObject(); - assertNotNull(obj2); - } catch(final NoSuchElementException e) { - // Not expected - fail("NoSuchElementException not expected"); + private class ConcurrentBorrowAndEvictThread extends Thread { + private final boolean borrow; + public String obj; + + public ConcurrentBorrowAndEvictThread(final boolean borrow) { + this.borrow = borrow; } - genericObjectPool.returnObject(obj2); - genericObjectPool.close(); + @Override + public void run() { + try { + if (borrow) { + obj = genericObjectPool.borrowObject(); + } else { + genericObjectPool.evict(); + } + } catch (final Exception e) { /* Ignore */} + } } - @Test(timeout=60000) - public void testEvictWhileEmpty() throws Exception { - genericObjectPool.evict(); - genericObjectPool.evict(); - genericObjectPool.close(); - } + private static class CreateErrorFactory extends BasePooledObjectFactory<String> { - /** - * Tests addObject contention between ensureMinIdle triggered by - * the Evictor with minIdle > 0 and borrowObject. - * - * @throws Exception May occur in some failure modes - */ - @Test(timeout=60000) - public void testEvictAddObjects() throws Exception { - simpleFactory.setMakeLatency(300); - simpleFactory.setMaxTotal(2); - genericObjectPool.setMaxTotal(2); - genericObjectPool.setMinIdle(1); - genericObjectPool.borrowObject(); // numActive = 1, numIdle = 0 - // Create a test thread that will run once and try a borrow after - // 150ms fixed delay - final TestThread<String> borrower = new TestThread<>(genericObjectPool, 1, 150, false); - final Thread borrowerThread = new Thread(borrower); - // Set evictor to run in 100 ms - will create idle instance - genericObjectPool.setTimeBetweenEvictionRunsMillis(100); - borrowerThread.start(); // Off to the races - borrowerThread.join(); - assertTrue(!borrower.failed()); - } + private final Semaphore semaphore = new Semaphore(0); - @Test(timeout=60000) - public void testEvictLIFO() throws Exception { - checkEvict(true); - } + @Override + public String create() throws Exception { + semaphore.acquire(); + throw new UnknownError("wiggle"); + } - @Test(timeout=60000) - public void testEvictFIFO() throws Exception { - checkEvict(false); - } + public boolean hasQueuedThreads() { + return semaphore.hasQueuedThreads(); + } - private void checkEvict(final boolean lifo) throws Exception { - // yea this is hairy but it tests all the code paths in GOP.evict() - genericObjectPool.setSoftMinEvictableIdleTimeMillis(10); - genericObjectPool.setMinIdle(2); - genericObjectPool.setTestWhileIdle(true); - genericObjectPool.setLifo(lifo); - PoolUtils.prefill(genericObjectPool, 5); - genericObjectPool.evict(); - simpleFactory.setEvenValid(false); - simpleFactory.setOddValid(false); - simpleFactory.setThrowExceptionOnActivate(true); - genericObjectPool.evict(); - PoolUtils.prefill(genericObjectPool, 5); - simpleFactory.setThrowExceptionOnActivate(false); - simpleFactory.setThrowExceptionOnPassivate(true); - genericObjectPool.evict(); - simpleFactory.setThrowExceptionOnPassivate(false); - simpleFactory.setEvenValid(true); - simpleFactory.setOddValid(true); - Thread.sleep(125); - genericObjectPool.evict(); - assertEquals(2, genericObjectPool.getNumIdle()); - } + public void release() { + semaphore.release(); + } - /** - * 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=60000) - public void testEvictionOrder() throws Exception { - checkEvictionOrder(false); - tearDown(); - setUp(); - checkEvictionOrder(true); + @Override + public PooledObject<String> wrap(final String obj) { + return new DefaultPooledObject<>(obj); + } } - private void checkEvictionOrder(final boolean lifo) throws Exception { - checkEvictionOrderPart1(lifo); - tearDown(); - setUp(); - checkEvictionOrderPart2(lifo); + private static class CreateFailFactory extends BasePooledObjectFactory<String> { + + private final Semaphore semaphore = new Semaphore(0); + + @Override + public String create() throws Exception { + semaphore.acquire(); + throw new UnsupportedCharsetException("wibble"); + } + + public boolean hasQueuedThreads() { + return semaphore.hasQueuedThreads(); + } + + public void release() { + semaphore.release(); + } + + @Override + public PooledObject<String> wrap(final String obj) { + return new DefaultPooledObject<>(obj); + } } - private void checkEvictionOrderPart1(final boolean lifo) throws Exception { - genericObjectPool.setNumTestsPerEvictionRun(2); - genericObjectPool.setMinEvictableIdleTimeMillis(100); - genericObjectPool.setLifo(lifo); - for (int i = 0; i < 5; i++) { - genericObjectPool.addObject(); - Thread.sleep(100); + private static final class DummyFactory + extends BasePooledObjectFactory<Object> { + @Override + public Object create() throws Exception { + return null; + } + @Override + public PooledObject<Object> wrap(final Object value) { + return new DefaultPooledObject<>(value); } - // Order, oldest to youngest, is "0", "1", ...,"4" - genericObjectPool.evict(); // Should evict "0" and "1" - final Object obj = genericObjectPool.borrowObject(); - assertTrue("oldest not evicted", !obj.equals("0")); - assertTrue("second oldest not evicted", !obj.equals("1")); - // 2 should be next out for FIFO, 4 for LIFO - assertEquals("Wrong instance returned", lifo ? "4" : "2" , obj); } - private void checkEvictionOrderPart2(final boolean lifo) throws Exception { - // Two eviction runs in sequence - genericObjectPool.setNumTestsPerEvictionRun(2); - genericObjectPool.setMinEvictableIdleTimeMillis(100); - genericObjectPool.setLifo(lifo); - for (int i = 0; i < 5; i++) { - genericObjectPool.addObject(); - Thread.sleep(100); + private static class EvictionThread<T> extends Thread { + + private final GenericObjectPool<T> pool; + + public EvictionThread(final GenericObjectPool<T> pool) { + this.pool = pool; + } + + @Override + public void run() { + try { + pool.evict(); + } catch (final Exception e) { + // Ignore + } } - genericObjectPool.evict(); // Should evict "0" and "1" - genericObjectPool.evict(); // Should evict "2" and "3" - final Object obj = genericObjectPool.borrowObject(); - assertEquals("Wrong instance remaining in pool", "4", obj); } /** - * Verifies that the evictor visits objects in expected order - * and frequency. - * - * @throws Exception May occur in some failure modes + * 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. */ - @Test - public void testEvictorVisiting() throws Exception { - checkEvictorVisiting(true); - checkEvictorVisiting(false); + private static final class HashSetFactory + extends BasePooledObjectFactory<HashSet<String>> { + @Override + public HashSet<String> create() throws Exception { + return new HashSet<>(); + } + @Override + public PooledObject<HashSet<String>> wrap(final HashSet<String> value) { + return new DefaultPooledObject<>(value); + } } - private void checkEvictorVisiting(final boolean lifo) throws Exception { - VisitTracker<Object> obj; - VisitTrackerFactory<Object> trackerFactory = new VisitTrackerFactory<>(); - try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) { - trackerPool.setNumTestsPerEvictionRun(2); - trackerPool.setMinEvictableIdleTimeMillis(-1); - trackerPool.setTestWhileIdle(true); - trackerPool.setLifo(lifo); - trackerPool.setTestOnReturn(false); - trackerPool.setTestOnBorrow(false); - for (int i = 0; i < 8; i++) { - trackerPool.addObject(); - } - trackerPool.evict(); // Visit oldest 2 - 0 and 1 - obj = trackerPool.borrowObject(); - trackerPool.returnObject(obj); - obj = trackerPool.borrowObject(); - trackerPool.returnObject(obj); - // borrow, return, borrow, return - // FIFO will move 0 and 1 to end - // LIFO, 7 out, then in, then out, then in - trackerPool.evict(); // Should visit 2 and 3 in either case - for (int i = 0; i < 8; i++) { - final VisitTracker<Object> tracker = trackerPool.borrowObject(); - if (tracker.getId() >= 4) { - assertEquals("Unexpected instance visited " + tracker.getId(), 0, tracker.getValidateCount()); - } else { - assertEquals("Instance " + tracker.getId() + " visited wrong number of times.", 1, - tracker.getValidateCount()); - } + /** + * Attempts to invalidate an object, swallowing IllegalStateException. + */ + static class InvalidateThread implements Runnable { + private final String obj; + private final ObjectPool<String> pool; + private boolean done = false; + public InvalidateThread(final ObjectPool<String> pool, final String obj) { + this.obj = obj; + this.pool = pool; + } + public boolean complete() { + return done; + } + @Override + public void run() { + try { + pool.invalidateObject(obj); + } catch (final IllegalStateException ex) { + // Ignore + } catch (final Exception ex) { + Assert.fail("Unexpected exception " + ex.toString()); + } finally { + done = true; } } + } - trackerFactory = new VisitTrackerFactory<>(); - try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) { - trackerPool.setNumTestsPerEvictionRun(3); - trackerPool.setMinEvictableIdleTimeMillis(-1); - trackerPool.setTestWhileIdle(true); - trackerPool.setLifo(lifo); - trackerPool.setTestOnReturn(false); - trackerPool.setTestOnBorrow(false); - for (int i = 0; i < 8; i++) { - trackerPool.addObject(); - } - trackerPool.evict(); // 0, 1, 2 - trackerPool.evict(); // 3, 4, 5 - obj = trackerPool.borrowObject(); - trackerPool.returnObject(obj); - obj = trackerPool.borrowObject(); - trackerPool.returnObject(obj); - obj = trackerPool.borrowObject(); - trackerPool.returnObject(obj); - // borrow, return, borrow, return - // FIFO 3,4,5,6,7,0,1,2 - // LIFO 7,6,5,4,3,2,1,0 - // In either case, pointer should be at 6 - trackerPool.evict(); - // Should hit 6,7,0 - 0 for second time - for (int i = 0; i < 8; i++) { - final VisitTracker<Object> tracker = trackerPool.borrowObject(); - if (tracker.getId() != 0) { - assertEquals("Instance " + tracker.getId() + " visited wrong number of times.", 1, - tracker.getValidateCount()); - } else { - assertEquals("Instance " + tracker.getId() + " visited wrong number of times.", 2, - tracker.getValidateCount()); - } - } - } - - // Randomly generate a pools with random numTests - // and make sure evictor cycles through elements appropriately - final int[] smallPrimes = { 2, 3, 5, 7 }; - final Random random = new Random(); - random.setSeed(System.currentTimeMillis()); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 5; j++) { - try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) { - trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]); - trackerPool.setMinEvictableIdleTimeMillis(-1); - trackerPool.setTestWhileIdle(true); - trackerPool.setLifo(lifo); - trackerPool.setTestOnReturn(false); - trackerPool.setTestOnBorrow(false); - trackerPool.setMaxIdle(-1); - final int instanceCount = 10 + random.nextInt(20); - trackerPool.setMaxTotal(instanceCount); - for (int k = 0; k < instanceCount; k++) { - trackerPool.addObject(); - } - - // Execute a random number of evictor runs - final int runs = 10 + random.nextInt(50); - for (int k = 0; k < runs; k++) { - trackerPool.evict(); - } - - // Number of times evictor should have cycled through the pool - final int cycleCount = (runs * trackerPool.getNumTestsPerEvictionRun()) / instanceCount; + private static class InvalidFactory + extends BasePooledObjectFactory<Object> { - // Look at elements and make sure they are visited cycleCount - // or cycleCount + 1 times - VisitTracker<Object> tracker = null; - int visitCount = 0; - for (int k = 0; k < instanceCount; k++) { - tracker = trackerPool.borrowObject(); - assertTrue(trackerPool.getNumActive() <= trackerPool.getMaxTotal()); - visitCount = tracker.getValidateCount(); - assertTrue(visitCount >= cycleCount && visitCount <= cycleCount + 1); - } - } + @Override + public Object create() throws Exception { + return new Object(); + } + @Override + public boolean validateObject(final PooledObject<Object> obj) { + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + // Ignore } + return false; } - } - - @Test(timeout=60000) - public void testExceptionOnPassivateDuringReturn() throws Exception { - final String obj = genericObjectPool.borrowObject(); - simpleFactory.setThrowExceptionOnPassivate(true); - genericObjectPool.returnObject(obj); - assertEquals(0,genericObjectPool.getNumIdle()); - } - @Test(timeout=60000) - public void testExceptionOnDestroyDuringBorrow() throws Exception { - simpleFactory.setThrowExceptionOnDestroy(true); - genericObjectPool.setTestOnBorrow(true); - genericObjectPool.borrowObject(); - simpleFactory.setValid(false); // Make validation fail on next borrow attempt - try { - genericObjectPool.borrowObject(); - fail("Expecting NoSuchElementException"); - } catch (final NoSuchElementException ex) { - // expected + @Override + public PooledObject<Object> wrap(final Object value) { + return new DefaultPooledObject<>(value); } - assertEquals(1, genericObjectPool.getNumActive()); - assertEquals(0, genericObjectPool.getNumIdle()); } - @Test(timeout=60000) - public void testExceptionOnDestroyDuringReturn() throws Exception { - simpleFactory.setThrowExceptionOnDestroy(true); - genericObjectPool.setTestOnReturn(true); - final String obj1 = genericObjectPool.borrowObject(); - genericObjectPool.borrowObject(); - simpleFactory.setValid(false); // Make validation fail - genericObjectPool.returnObject(obj1); - assertEquals(1, genericObjectPool.getNumActive()); - assertEquals(0, genericObjectPool.getNumIdle()); - } + public static class SimpleFactory implements PooledObjectFactory<String> { + int makeCounter = 0; - @Test(timeout=60000) - public void testExceptionOnActivateDuringBorrow() throws Exception { - final String obj1 = genericObjectPool.borrowObject(); - final String obj2 = genericObjectPool.borrowObject(); - genericObjectPool.returnObject(obj1); - genericObjectPool.returnObject(obj2); - simpleFactory.setThrowExceptionOnActivate(true); - simpleFactory.setEvenValid(false); - // Activation will now throw every other time - // First attempt throws, but loop continues and second succeeds - final String obj = genericObjectPool.borrowObject(); - assertEquals(1, genericObjectPool.getNumActive()); - assertEquals(0, genericObjectPool.getNumIdle()); + int activationCounter = 0; - genericObjectPool.returnObject(obj); - simpleFactory.setValid(false); - // Validation will now fail on activation when borrowObject returns - // an idle instance, and then when attempting to create a new instance - try { - genericObjectPool.borrowObject(); - fail("Expecting NoSuchElementException"); - } catch (final NoSuchElementException ex) { - // expected - } - assertEquals(0, genericObjectPool.getNumActive()); - assertEquals(0, genericObjectPool.getNumIdle()); - } + int validateCounter = 0; - @Test(timeout=60000) - public void testNegativeMaxTotal() throws Exception { - genericObjectPool.setMaxTotal(-1); - genericObjectPool.setBlockWhenExhausted(false); - final String obj = genericObjectPool.borrowObject(); - assertEquals(getNthObject(0),obj); - genericObjectPool.returnObject(obj); - } + int activeCount = 0; - @Test(timeout=60000) - public void testMaxIdle() throws Exception { - genericObjectPool.setMaxTotal(100); - genericObjectPool.setMaxIdle(8); - final String[] active = new String[100]; - for(int i=0;i<100;i++) { - active[i] = genericObjectPool.borrowObject(); - } - assertEquals(100,genericObjectPool.getNumActive()); - assertEquals(0,genericObjectPool.getNumIdle()); - for(int i=0;i<100;i++) { - genericObjectPool.returnObject(active[i]); - assertEquals(99 - i,genericObjectPool.getNumActive()); - assertEquals((i < 8 ? i+1 : 8),genericObjectPool.getNumIdle()); - } - } + boolean evenValid = true; - @Test(timeout=60000) - public void testMaxIdleZero() throws Exception { - genericObjectPool.setMaxTotal(100); - genericObjectPool.setMaxIdle(0); - final String[] active = new String[100]; - for(int i=0;i<100;i++) { - active[i] = genericObjectPool.borrowObject(); - } - assertEquals(100,genericObjectPool.getNumActive()); - assertEquals(0,genericObjectPool.getNumIdle()); - for(int i=0;i<100;i++) { - genericObjectPool.returnObject(active[i]); - assertEquals(99 - i,genericObjectPool.getNumActive()); - assertEquals(0, genericObjectPool.getNumIdle()); - } - } + boolean oddValid = true; - @Test(timeout=60000) - public void testMaxTotal() throws Exception { - genericObjectPool.setMaxTotal(3); - genericObjectPool.setBlockWhenExhausted(false); + boolean exceptionOnPassivate = false; - genericObjectPool.borrowObject(); - genericObjectPool.borrowObject(); - genericObjectPool.borrowObject(); - try { - genericObjectPool.borrowObject(); - fail("Expected NoSuchElementException"); - } catch(final NoSuchElementException e) { - // expected - } - } + boolean exceptionOnActivate = false; - @Test(timeout=60000) - public void testTimeoutNoLeak() throws Exception { - genericObjectPool.setMaxTotal(2); - genericObjectPool.setMaxWaitMillis(10); - genericObjectPool.setBlockWhenExhausted(true); - final String obj = genericObjectPool.borrowObject(); - final String obj2 = genericObjectPool.borrowObject(); - try { - genericObjectPool.borrowObject(); - fail("Expecting NoSuchElementException"); - } catch (final NoSuchElementException ex) { - // expected - } - genericObjectPool.returnObject(obj2); - genericObjectPool.returnObject(obj); + boolean exceptionOnDestroy = false; - genericObjectPool.borrowObject(); - genericObjectPool.borrowObject(); - } + boolean enableValidation = true; - @Test(timeout=60000) - public void testMaxTotalZero() throws Exception { - genericObjectPool.setMaxTotal(0); - genericObjectPool.setBlockWhenExhausted(false); + long destroyLatency = 0; - try { - genericObjectPool.borrowObject(); - fail("Expected NoSuchElementException"); - } catch(final NoSuchElementException e) { - // expected - } - } + long makeLatency = 0; - @Test(timeout=60000) - @SuppressWarnings("rawtypes") - public void testMaxTotalUnderLoad() { - // Config - final int numThreads = 199; // And main thread makes a round 200. - final int numIter = 20; - final int delay = 25; - final int maxTotal = 10; + long validateLatency = 0; - simpleFactory.setMaxTotal(maxTotal); - genericObjectPool.setMaxTotal(maxTotal); - genericObjectPool.setBlockWhenExhausted(true); - genericObjectPool.setTimeBetweenEvictionRunsMillis(-1); + int maxTotal = Integer.MAX_VALUE; - // Start threads to borrow objects - final TestThread[] threads = new TestThread[numThreads]; - for(int i=0;i<numThreads;i++) { - // Factor of 2 on iterations so main thread does work whilst other - // threads are running. Factor of 2 on delay so average delay for - // other threads == actual delay for main thread - threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2); - final Thread t = new Thread(threads[i]); - t.start(); - } - // Give the threads a chance to start doing some work - try { - Thread.sleep(5000); - } catch(final InterruptedException e) { - // ignored + public SimpleFactory() { + this(true); } - for (int i = 0; i < numIter; i++) { - String obj = null; - try { - try { - Thread.sleep(delay); - } catch(final InterruptedException e) { - // ignored - } - obj = genericObjectPool.borrowObject(); - // Under load, observed _numActive > _maxTotal - if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) { - throw new IllegalStateException("Too many active objects"); - } - try { - Thread.sleep(delay); - } catch(final InterruptedException e) { - // ignored - } - } catch (final Exception e) { - // Shouldn't happen - e.printStackTrace(); - fail("Exception on borrow"); - } finally { - if (obj != null) { - try { - genericObjectPool.returnObject(obj); - } catch (final Exception e) { - // Ignore - } + public SimpleFactory(final boolean valid) { + this(valid,valid); + } + public SimpleFactory(final boolean evalid, final boolean ovalid) { + evenValid = evalid; + oddValid = ovalid; + } + @Override + public void activateObject(final PooledObject<String> obj) throws Exception { + final boolean hurl; + final boolean evenTest; + final boolean oddTest; + final int counter; + synchronized(this) { + hurl = exceptionOnActivate; + evenTest = evenValid; + oddTest = oddValid; + counter = activationCounter++; + } + if (hurl) { + if (!(counter%2 == 0 ? evenTest : oddTest)) { + throw new Exception(); } } } - - for(int i=0;i<numThreads;i++) { - while(!(threads[i]).complete()) { - try { - Thread.sleep(500L); - } catch(final InterruptedException e) { - // ignored + @Override + public void destroyObject(final PooledObject<String> obj) throws Exception { + final long waitLatency; + final boolean hurl; + synchronized(this) { + waitLatency = destroyLatency; + hurl = exceptionOnDestroy; + } + if (waitLatency > 0) { + doWait(waitLatency); + } + synchronized(this) { + activeCount--; + } + if (hurl) { + throw new Exception(); + } + } + private void doWait(final long latency) { + try { + Thread.sleep(latency); + } catch (final InterruptedException ex) { + // ignore + } + } + public synchronized int getMakeCounter() { + return makeCounter; + } + public synchronized boolean isThrowExceptionOnActivate() { + return exceptionOnActivate; + } + public synchronized boolean isValidationEnabled() { + return enableValidation; + } + @Override + public PooledObject<String> makeObject() { + final long waitLatency; + synchronized(this) { + activeCount++; + if (activeCount > maxTotal) { + throw new IllegalStateException( + "Too many active instances: " + activeCount); } + waitLatency = makeLatency; } - if(threads[i].failed()) { - fail("Thread "+i+" failed: "+threads[i]._error.toString()); + if (waitLatency > 0) { + doWait(waitLatency); + } + final int counter; + synchronized(this) { + counter = makeCounter++; } + return new DefaultPooledObject<>(String.valueOf(counter)); + } + @Override + public void passivateObject(final PooledObject<String> obj) throws Exception { + final boolean hurl; + synchronized(this) { + hurl = exceptionOnPassivate; + } + if (hurl) { + throw new Exception(); + } + } + public synchronized void setDestroyLatency(final long destroyLatency) { + this.destroyLatency = destroyLatency; + } + public synchronized void setEvenValid(final boolean valid) { + evenValid = valid; + } + public synchronized void setMakeLatency(final long makeLatency) { + this.makeLatency = makeLatency; + } + public synchronized void setMaxTotal(final int maxTotal) { + this.maxTotal = maxTotal; + } + public synchronized void setOddValid(final boolean valid) { + oddValid = valid; } - } - /** - * Showcasing a possible deadlock situation as reported in POOL-356 - */ - @Test(timeout=60000) - @SuppressWarnings("rawtypes") - public void testMaxIdleZeroUnderLoad() { - // Config - final int numThreads = 199; // And main thread makes a round 200. - final int numIter = 20; - final int delay = 25; - final int maxTotal = 10; + public synchronized void setThrowExceptionOnActivate(final boolean b) { + exceptionOnActivate = b; + } - simpleFactory.setMaxTotal(maxTotal); - genericObjectPool.setMaxTotal(maxTotal); - genericObjectPool.setBlockWhenExhausted(true); - genericObjectPool.setTimeBetweenEvictionRunsMillis(-1); + public synchronized void setThrowExceptionOnDestroy(final boolean b) { + exceptionOnDestroy = b; + } - // this is important to trigger POOL-356 - genericObjectPool.setMaxIdle(0); + public synchronized void setThrowExceptionOnPassivate(final boolean bool) { + exceptionOnPassivate = bool; + } - // Start threads to borrow objects - final TestThread[] threads = new TestThread[numThreads]; - for(int i=0;i<numThreads;i++) { - // Factor of 2 on iterations so main thread does work whilst other - // threads are running. Factor of 2 on delay so average delay for - // other threads == actual delay for main thread - threads[i] = new TestThread<>(genericObjectPool, numIter * 2, delay * 2); - final Thread t = new Thread(threads[i]); - t.start(); + public synchronized void setValid(final boolean valid) { + setEvenValid(valid); + setOddValid(valid); } - // Give the threads a chance to start doing some work - try { - Thread.sleep(100); - } catch(final InterruptedException e) { - // ignored + + public synchronized void setValidateLatency(final long validateLatency) { + this.validateLatency = validateLatency; } - for (int i = 0; i < numIter; i++) { - String obj = null; - try { - try { - Thread.sleep(delay); - } catch(final InterruptedException e) { - // ignored - } - obj = genericObjectPool.borrowObject(); - // Under load, observed _numActive > _maxTotal - if (genericObjectPool.getNumActive() > genericObjectPool.getMaxTotal()) { - throw new IllegalStateException("Too many active objects"); - } - try { - Thread.sleep(delay); - } catch(final InterruptedException e) { - // ignored - } - } catch (final Exception e) { - // Shouldn't happen - e.printStackTrace(); - fail("Exception on borrow"); - } finally { - if (obj != null) { - try { - genericObjectPool.returnObject(obj); - } catch (final Exception e) { - // Ignore - } - } - } + public synchronized void setValidationEnabled(final boolean b) { + enableValidation = b; } - for(int i=0;i<numThreads;i++) { - while(!(threads[i]).complete()) { - try { - Thread.sleep(500L); - } catch(final InterruptedException e) { - // ignored - } + @Override + public boolean validateObject(final PooledObject<String> obj) { + final boolean validate; + final boolean evenTest; + final boolean oddTest; + final long waitLatency; + final int counter; + synchronized(this) { + validate = enableValidation; + evenTest = evenValid; + oddTest = oddValid; + counter = validateCounter++; + waitLatency = validateLatency; } - if(threads[i].failed()) { - threads[i]._error.printStackTrace(); - fail("Thread "+i+" failed: "+threads[i]._error.toString()); + if (waitLatency > 0) { + doWait(waitLatency); + } + if (validate) { + return counter%2 == 0 ? evenTest : oddTest; } + return true; } } - /** - * This is the test case for POOL-263. It is disabled since it will always - * pass without artificial delay being injected into GOP.returnObject() and - * a way to this hasn't currently been found that doesn't involve - * polluting the GOP implementation. The artificial delay needs to be - * inserted just before the final call to isLifo() in the returnObject() - * method. - */ - //@Test(timeout=60000) - public void testReturnObject() throws Exception { + public static class TestEvictionPolicy<T> implements EvictionPolicy<T> { - genericObjectPool.setMaxTotal(1); - genericObjectPool.setMaxIdle(-1); - final String active = genericObjectPool.borrowObject(); + private final AtomicInteger callCount = new AtomicInteger(0); - assertEquals(1, genericObjectPool.getNumActive()); - assertEquals(0, genericObjectPool.getNumIdle()); + @Override + public boolean evict(final EvictionConfig config, final PooledObject<T> underTest, + final int idleCount) { + if (callCount.incrementAndGet() > 1500) { + return true; + } + return false; + } + } - final Thread t = new Thread() { + static class TestThread<T> implements Runnable { - @Override - public void run() { - genericObjectPool.close(); - } + /** source of random delay times */ + private final java.util.Random _random; - }; - t.start(); + /** pool to borrow from */ + private final ObjectPool<T> _pool; - genericObjectPool.returnObject(active); + /** number of borrow attempts */ + private final int _iter; - // Wait for the close() thread to complete - while (t.isAlive()) { - Thread.sleep(50); - } + /** delay before each borrow attempt */ + private final int _startDelay; - assertEquals(0, genericObjectPool.getNumIdle()); - } + /** time to hold each borrowed object before returning it */ + private final int _holdTime; + + /** whether or not start and hold time are randomly generated */ + private final boolean _randomDelay; + /** object expected to be borrowed (fail otherwise) */ + private final Object _expectedObject; - @Test(timeout=60000) - public void testSettersAndGetters() throws Exception { - { - // The object receives an Exception during its creation to prevent - // memory leaks. See BaseGenericObjectPool constructor for more details. - assertTrue(false == "".equals(genericObjectPool.getCreationStackTrace())); - } - { - assertEquals(0, genericObjectPool.getBorrowedCount()); - } - { - assertEquals(0, genericObjectPool.getReturnedCount()); - } - { - assertEquals(0, genericObjectPool.getCreatedCount()); - } - { - assertEquals(0, genericObjectPool.getDestroyedCount()); - } - { - assertEquals(0, genericObjectPool.getDestroyedByEvictorCount()); - } - { - assertEquals(0, genericObjectPool.getDestroyedByBorrowValidationCount()); - } - { - assertEquals(0, genericObjectPool.getMeanActiveTimeMillis()); - } - { - assertEquals(0, genericObjectPool.getMeanIdleTimeMillis()); - } - { - assertEquals(0, genericObjectPool.getMeanBorrowWaitTimeMillis()); - } - { - assertEquals(0, genericObjectPool.getMaxBorrowWaitTimeMillis()); - } - { - assertEquals(0, genericObjectPool.getNumIdle()); - } - { - genericObjectPool.setMaxTotal(123); - assertEquals(123,genericObjectPool.getMaxTotal()); - } - { - genericObjectPool.setMaxIdle(12); - assertEquals(12,genericObjectPool.getMaxIdle()); + private volatile boolean _complete = false; + private volatile boolean _failed = false; + private volatile Throwable _error; + + public TestThread(final ObjectPool<T> pool) { + this(pool, 100, 50, true, null); } - { - genericObjectPool.setMaxWaitMillis(1234L); - assertEquals(1234L,genericObjectPool.getMaxWaitMillis()); + + public TestThread(final ObjectPool<T> pool, final int iter) { + this(pool, iter, 50, true, null); } - { - genericObjectPool.setMinEvictableIdleTimeMillis(12345L); - assertEquals(12345L,genericObjectPool.getMinEvictableIdleTimeMillis()); + + public TestThread(final ObjectPool<T> pool, final int iter, final int delay) { + this(pool, iter, delay, true, null); } - { - genericObjectPool.setNumTestsPerEvictionRun(11); - assertEquals(11,genericObjectPool.getNumTestsPerEvictionRun()); + + public TestThread(final ObjectPool<T> pool, final int iter, final int delay, + final boolean randomDelay) { + this(pool, iter, delay, randomDelay, null); } - { - genericObjectPool.setTestOnBorrow(true); - assertTrue(genericObjectPool.getTestOnBorrow()); - genericObjectPool.setTestOnBorrow(false); - assertTrue(!genericObjectPool.getTestOnBorrow()); + + public TestThread(final ObjectPool<T> pool, final int iter, final int delay, + final boolean randomDelay, final Object obj) { + this(pool, iter, delay, delay, randomDelay, obj); } - { - genericObjectPool.setTestOnReturn(true); - assertTrue(genericObjectPool.getTestOnReturn()); - genericObjectPool.setTestOnReturn(false); - assertTrue(!genericObjectPool.getTestOnReturn()); + + public TestThread(final ObjectPool<T> pool, final int iter, final int startDelay, + final int holdTime, final boolean randomDelay, final Object obj) { + _pool = pool; + _iter = iter; + _startDelay = startDelay; + _holdTime = holdTime; + _randomDelay = randomDelay; + _random = _randomDelay ? new Random() : null; + _expectedObject = obj; + } + + public boolean complete() { + return _complete; } - { - genericObjectPool.setTestWhileIdle(true); - assertTrue(genericObjectPool.getTestWhileIdle()); - genericObjectPool.setTestWhileIdle(false); - assertTrue(!genericObjectPool.getTestWhileIdle()); + + public boolean failed() { + return _failed; } - { - genericObjectPool.setTimeBetweenEvictionRunsMillis(11235L); - assertEquals(11235L,genericObjectPool.getTimeBetweenEvictionRunsMillis()); + + @Override + public void run() { + for(int i=0;i<_iter;i++) { + final long startDelay = + _randomDelay ? (long)_random.nextInt(_startDelay) : _startDelay; + final long holdTime = + _randomDelay ? (long)_random.nextInt(_holdTime) : _holdTime; + try { + Thread.sleep(startDelay); + } catch(final InterruptedException e) { + // ignored + } + T obj = null; + try { + obj = _pool.borrowObject(); + } catch(final Exception e) { + _error = e; + _failed = true; + _complete = true; + break; + } + + if (_expectedObject != null && !_expectedObject.equals(obj)) { + _error = new Throwable("Expected: "+_expectedObject+ " found: "+obj); + _failed = true; + _complete = true; + break; + } + + try { + Thread.sleep(holdTime); + } catch(final InterruptedException e) { + // ignored + } + try { + _pool.returnObject(obj); + } catch(final Exception e) { + _error = e; + _failed = true; + _complete = true; + break; + } + } + _complete = true; } - { - genericObjectPool.setSoftMinEvictableIdleTimeMillis(12135L); - assertEquals(12135L,genericObjectPool.getSoftMinEvictableIdleTimeMillis()); + } + + /* + * Very simple test thread that just tries to borrow an object from + * the provided pool returns it after a wait + */ + static class WaitingTestThread extends Thread { + private final GenericObjectPool<String> _pool; + private final long _pause; + private Throwable _thrown; + + private long preborrow; // just before borrow + private long postborrow; // borrow returned + private long postreturn; // after object was returned + private long ended; + private String objectId; + + public WaitingTestThread(final GenericObjectPool<String> pool, final long pause) { + _pool = pool; + _pause = pause; + _thrown = null; } - { - genericObjectPool.setBlockWhenExhausted(true); - assertTrue(genericObjectPool.getBlockWhenExhausted()); - genericObjectPool.setBlockWhenExhausted(false); - assertFalse(genericObjectPool.getBlockWhenExhausted()); + + @Override + public void run() { + try { + preborrow = System.currentTimeMillis(); + final String obj = _pool.borrowObject(); + objectId = obj; + postborrow = System.currentTimeMillis(); + Thread.sleep(_pause); + _pool.returnObject(obj); + postreturn = System.currentTimeMillis(); + } catch (final Throwable e) { + _thrown = e; + } finally{ + ended = System.currentTimeMillis(); + } } } - @Test(timeout=60000) - public void testDefaultConfiguration() throws Exception { - assertConfiguration(new GenericObjectPoolConfig(),genericObjectPool); + private static final boolean DISPLAY_THREAD_DETAILS= + Boolean.valueOf(System.getProperty("TestGenericObjectPool.display.thread.details", "false")).booleanValue(); + // To pass this to a Maven test, use: + // mvn test -DargLine="-DTestGenericObjectPool.display.thread.details=true" + // @see http://jira.codehaus.org/browse/SUREFIRE-121 + + protected GenericObjectPool<String> genericObjectPool = null; + + private SimpleFactory simpleFactory = null; + + private void assertConfiguration(final GenericObjectPoolConfig expected, final GenericObjectPool<?> actual) throws Exception { + assertEquals("testOnCreate",Boolean.valueOf(expected.getTestOnCreate()), + Boolean.valueOf(actual.getTestOnCreate())); + assertEquals("testOnBorrow",Boolean.valueOf(expected.getTestOnBorrow()), + Boolean.valueOf(actual.getTestOnBorrow())); + assertEquals("testOnReturn",Boolean.valueOf(expected.getTestOnReturn()), + Boolean.valueOf(actual.getTestOnReturn())); + assertEquals("testWhileIdle",Boolean.valueOf(expected.getTestWhileIdle()), + Boolean.valueOf(actual.getTestWhileIdle())); + assertEquals("whenExhaustedAction", + Boolean.valueOf(expected.getBlockWhenExhausted()), + Boolean.valueOf(actual.getBlockWhenExhausted())); + assertEquals("maxTotal",expected.getMaxTotal(),actual.getMaxTotal()); + assertEquals("maxIdle",expected.getMaxIdle(),actual.getMaxIdle()); + assertEquals("maxWait",expected.getMaxWaitMillis(),actual.getMaxWaitMillis()); + assertEquals("minEvictableIdleTimeMillis",expected.getMinEvictableIdleTimeMillis(),actual.getMinEvictableIdleTimeMillis()); + assertEquals("numTestsPerEvictionRun",expected.getNumTestsPerEvictionRun(),actual.getNumTestsPerEvictionRun()); + assertEquals("evictorShutdownTimeoutMillis",expected.getEvictorShutdownTimeoutMillis(),actual.getEvictorShutdownTimeoutMillis()); + assertEquals("timeBetweenEvictionRunsMillis",expected.getTimeBetweenEvictionRunsMillis(),actual.getTimeBetweenEvictionRunsMillis()); } - @Test(timeout=60000) - public void testSetConfig() throws Exception { - final GenericObjectPoolConfig expected = new GenericObjectPoolConfig(); - assertConfiguration(expected,genericObjectPool); - expected.setMaxTotal(2); - expected.setMaxIdle(3); - expected.setMaxWaitMillis(5L); - expected.setMinEvictableIdleTimeMillis(7L); - expected.setNumTestsPerEvictionRun(9); - expected.setTestOnCreate(true); - expected.setTestOnBorrow(true); - expected.setTestOnReturn(true); - expected.setTestWhileIdle(true); - expected.setTimeBetweenEvictionRunsMillis(11L); - expected.setBlockWhenExhausted(false); - genericObjectPool.setConfig(expected); - assertConfiguration(expected,genericObjectPool); + private void checkEvict(final boolean lifo) throws Exception { + // yea this is hairy but it tests all the code paths in GOP.evict() + genericObjectPool.setSoftMinEvictableIdleTimeMillis(10); + genericObjectPool.setMinIdle(2); + genericObjectPool.setTestWhileIdle(true); + genericObjectPool.setLifo(lifo); + PoolUtils.prefill(genericObjectPool, 5); + genericObjectPool.evict(); + simpleFactory.setEvenValid(false); + simpleFactory.setOddValid(false); + simpleFactory.setThrowExceptionOnActivate(true); + genericObjectPool.evict(); + PoolUtils.prefill(genericObjectPool, 5); + simpleFactory.setThrowExceptionOnActivate(false); + simpleFactory.setThrowExceptionOnPassivate(true); + genericObjectPool.evict(); + simpleFactory.setThrowExceptionOnPassivate(false); + simpleFactory.setEvenValid(true); + simpleFactory.setOddValid(true); + Thread.sleep(125); + genericObjectPool.evict(); + assertEquals(2, genericObjectPool.getNumIdle()); } - @Test(timeout=60000) - public void testStartAndStopEvictor() throws Exception { - // set up pool without evictor - genericObjectPool.setMaxIdle(6); - genericObjectPool.setMaxTotal(6); - genericObjectPool.setNumTestsPerEvictionRun(6); - genericObjectPool.setMinEvictableIdleTimeMillis(100L); + private void checkEvictionOrder(final boolean lifo) throws Exception { + checkEvictionOrderPart1(lifo); + tearDown(); + setUp(); + checkEvictionOrderPart2(lifo); + } - for(int j=0;j<2;j++) { - // populate the pool - { - final String[] active = new String[6]; - for(int i=0;i<6;i++) { - active[i] = genericObjectPool.borrowObject(); + private void checkEvictionOrderPart1(final boolean lifo) throws Exception { + genericObjectPool.setNumTestsPerEvictionRun(2); + genericObjectPool.setMinEvictableIdleTimeMillis(100); + genericObjectPool.setLifo(lifo); + for (int i = 0; i < 5; i++) { + genericObjectPool.addObject(); + Thread.sleep(100); + } + // Order, oldest to youngest, is "0", "1", ...,"4" + genericObjectPool.evict(); // Should evict "0" and "1" + final Object obj = genericObjectPool.borrowObject(); + assertTrue("oldest not evicted", !obj.equals("0")); + assertTrue("second oldest not evicted", !obj.equals("1")); + // 2 should be next out for FIFO, 4 for LIFO + assertEquals("Wrong instance returned", lifo ? "4" : "2" , obj); + } + + private void checkEvictionOrderPart2(final boolean lifo) throws Exception { + // Two eviction runs in sequence + genericObjectPool.setNumTestsPerEvictionRun(2); + genericObjectPool.setMinEvictableIdleTimeMillis(100); + genericObjectPool.setLifo(lifo); + for (int i = 0; i < 5; i++) { + genericObjectPool.addObject(); + Thread.sleep(100); + } + genericObjectPool.evict(); // Should evict "0" and "1" + genericObjectPool.evict(); // Should evict "2" and "3" + final Object obj = genericObjectPool.borrowObject(); + assertEquals("Wrong instance remaining in pool", "4", obj); + } + + private void checkEvictorVisiting(final boolean lifo) throws Exception { + VisitTracker<Object> obj; + VisitTrackerFactory<Object> trackerFactory = new VisitTrackerFactory<>(); + try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) { + trackerPool.setNumTestsPerEvictionRun(2); + trackerPool.setMinEvictableIdleTimeMillis(-1); + trackerPool.setTestWhileIdle(true); + trackerPool.setLifo(lifo); + trackerPool.setTestOnReturn(false); + trackerPool.setTestOnBorrow(false); + for (int i = 0; i < 8; i++) { + trackerPool.addObject(); + } + trackerPool.evict(); // Visit oldest 2 - 0 and 1 + obj = trackerPool.borrowObject(); + trackerPool.returnObject(obj); + obj = trackerPool.borrowObject(); + trackerPool.returnObject(obj); + // borrow, return, borrow, return + // FIFO will move 0 and 1 to end + // LIFO, 7 out, then in, then out, then in + trackerPool.evict(); // Should visit 2 and 3 in either case + for (int i = 0; i < 8; i++) { + final VisitTracker<Object> tracker = trackerPool.borrowObject(); + if (tracker.getId() >= 4) { + assertEquals("Unexpected instance visited " + tracker.getId(), 0, tracker.getValidateCount()); + } else { + assertEquals("Instance " + tracker.getId() + " visited wrong number of times.", 1, + tracker.getValidateCount()); } - for(int i=0;i<6;i++) { - genericObjectPool.returnObject(active[i]); + } + } + + trackerFactory = new VisitTrackerFactory<>(); + try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) { + trackerPool.setNumTestsPerEvictionRun(3); + trackerPool.setMinEvictableIdleTimeMillis(-1); + trackerPool.setTestWhileIdle(true); + trackerPool.setLifo(lifo); + trackerPool.setTestOnReturn(false); + trackerPool.setTestOnBorrow(false); + for (int i = 0; i < 8; i++) { + trackerPool.addObject(); + } + trackerPool.evict(); // 0, 1, 2 + trackerPool.evict(); // 3, 4, 5 + obj = trackerPool.borrowObject(); + trackerPool.returnObject(obj); + obj = trackerPool.borrowObject(); + trackerPool.returnObject(obj); + obj = trackerPool.borrowObject(); + trackerPool.returnObject(obj); + // borrow, return, borrow, return + // FIFO 3,4,5,6,7,0,1,2 + // LIFO 7,6,5,4,3,2,1,0 + // In either case, pointer should be at 6 + trackerPool.evict(); + // Should hit 6,7,0 - 0 for second time + for (int i = 0; i < 8; i++) { + final VisitTracker<Object> tracker = trackerPool.borrowObject(); + if (tracker.getId() != 0) { + assertEquals("Instance " + tracker.getId() + " visited wrong number of times.", 1, + tracker.getValidateCount()); + } else { + assertEquals("Instance " + tracker.getId() + " visited wrong number of times.", 2, + tracker.getValidateCount()); } } + } - // note that it stays populated - assertEquals("Should have 6 idle",6,genericObjectPool.getNumIdle()); - - // start the evictor - genericObjectPool.setTimeBetweenEvictionRunsMillis(50L); + // Randomly generate a pools with random numTests + // and make sure evictor cycles through elements appropriately + final int[] smallPrimes = { 2, 3, 5, 7 }; + final Random random = new Random(); + random.setSeed(System.currentTimeMillis()); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 5; j++) { + try (GenericObjectPool<VisitTracker<Object>> trackerPool = new GenericObjectPool<>(trackerFactory)) { + trackerPool.setNumTestsPerEvictionRun(smallPrimes[i]); + trackerPool.setMinEvictableIdleTimeMillis(-1); + trackerPool.setTestWhileIdle(true); + trackerPool.setLifo(lifo); + trackerPool.setTestOnReturn(false); + trackerPool.setTestOnBorrow(false); + trackerPool.setMaxIdle(-1); + final int instanceCount = 10 + random.nextInt(20); + trackerPool.setMaxTotal(instanceCount); + for (int k = 0; k < instanceCount; k++) { + trackerPool.addObject(); + } - // wait a second (well, .2 seconds) - try { Thread.sleep(200L); } catch(final InterruptedException e) { } + // Execute a random number of evictor runs + final int runs = 10 + random.nextInt(50); + for (int k = 0; k < runs; k++) { + trackerPool.evict(); + } - // assert that the evictor has cleared out the pool - assertEquals("Should have 0 idle",0,genericObjectPool.getNumIdle()); + // Number of times evictor should have cycled through the pool + final int cycleCount = (runs * trackerPool.getNumTestsPerEvictionRun()) / instanceCount; - // stop the evictor - genericObjectPool.startEvictor(0L); + // Look at elements and make sure they are visited cycleCount + // or cycleCount + 1 times + VisitTracker<Object> tracker = null; + int visitCount = 0; + for (int k = 0; k < instanceCount; k++) { + tracker = trackerPool.borrowObject(); + assertTrue(trackerPool.getNumActive() <= trackerPool.getMaxTotal()); + visitCount = tracker.getValidateCount(); + assertTrue(visitCount >= cycleCount && visitCount <= cycleCount + 1); + } + } + } } } - @Test(timeout=60000) - public void testEvictionWithNegativeNumTests() throws Exception { - // when numTestsPerEvictionRun is negative, it represents a fraction of the idle objects to test - genericObjectPool.setMaxIdle(6); - genericObjectPool.setMaxTotal(6); - genericObjectPool.setNumTestsPerEvictionRun(-2); - genericObjectPool.setMinEvictableIdleTimeMillis(50L); - genericObjectPool.setTimeBetweenEvictionRunsMillis(100L); - - final String[] active = new String[6]; - for(int i=0;i<6;i++) { - active[i] = genericObjectPool.borrowObject(); - } - for(int i=0;i<6;i++) { - genericObjectPool.returnObject(active[i]); - } + private BasePooledObjectFactory<String> createDefaultPooledObjectFactory() { + return new BasePooledObjectFactory<String>() { + @Override + public String create() { + // fake + return null; + } - try { Thread.sleep(100L); } catch(final InterruptedException e) { } - assertTrue("Should at most 6 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() <= 6); - try { Thread.sleep(100L); } catch(final InterruptedException e) { } - assertTrue("Should at most 3 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() <= 3); - try { Thread.sleep(100L); } catch(final InterruptedException e) { } - assertTrue("Should be at most 2 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() <= 2); - try { Thread.sleep(100L); } catch(final InterruptedException e) { } - assertEquals("Should be zero idle, found " + genericObjectPool.getNumIdle(),0,genericObjectPool.getNumIdle()); + @Override + public PooledObject<String> wrap(final String obj) { + // fake + return new DefaultPooledObject<>(obj); + } + }; } - @Test(timeout=60000) - public void testEviction() throws Exception { - genericObjectPool.setMaxIdle(500); - genericObjectPool.setMaxTotal(500); - genericObjectPool.setNumTestsPerEvictionRun(100); - genericObjectPool.setMinEvictableIdleTimeMillis(250L); - genericObjectPool.setTimeBetweenEvictionRunsMillis(500L); - genericObjectPool.setTestWhileIdle(true); - - final String[] active = new String[500]; - for(int i=0;i<500;i++) { - active[i] = genericObjectPool.borrowObject(); - } - for(int i=0;i<500;i++) { - genericObjectPool.returnObject(active[i]); - } + private BasePooledObjectFactory<String> createNullPooledObjectFactory() { + return new BasePooledObjectFactory<String>() { + @Override + public String create() { + // fake + return null; + } - try { Thread.sleep(1000L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 500 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 500); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 400 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 400); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 300 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 300); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 200 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 200); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 100 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 100); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertEquals("Should be zero idle, found " + genericObjectPool.getNumIdle(),0,genericObjectPool.getNumIdle()); + @Override + public PooledObject<String> wrap(final String obj) { + // fake + return null; + } + }; + } - for(int i=0;i<500;i++) { - active[i] = genericObjectPool.borrowObject(); - } - for(int i=0;i<500;i++) { - genericObjectPool.returnObject(active[i]); - } + private BasePooledObjectFactory<String> createSlowObjectFactory(final long elapsedTimeMillis) { + return new BasePooledObjectFactory<String>() { + @Override + public String create() throws Exception { + Thread.sleep(elapsedTimeMillis); + return "created"; + } - try { Thread.sleep(1000L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 500 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 500); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 400 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 400); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 300 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 300); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 200 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 200); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertTrue("Should be less than 100 idle, found " + genericObjectPool.getNumIdle(),genericObjectPool.getNumIdle() < 100); - try { Thread.sleep(600L); } catch(final InterruptedException e) { } - assertEquals("Should be zero idle, found " + genericObjectPool.getNumIdle(),0,genericObjectPool.getNumIdle()); + @Override + public PooledObject<String> wrap(final String obj) { + // fake + return new DefaultPooledObject<>(obj); + } + }; } - public static class TestEvictionPolicy<T> implements EvictionPolicy<T> { - - private final AtomicInteger callCount = new AtomicInteger(0); + @Override + protected Object getNthObject(final int n) { + return String.valueOf(n); + } - @Override - public boolean evict(final EvictionConfig config, final PooledObject<T> underTest, - final int idleCount) { - if (callCount.incrementAndGet() > 1500) { - return true; - } - return false; - } + @Override + protected boolean isFifo() { + return false; } - @Test(timeout=60000) - public void testEvictionPolicy() throws Exception { - genericObjectPool.setMaxIdle(500); - genericObjectPool.setMaxTotal(500); - genericObjectPool.setNumTestsPerEvictionRun(500); - genericObjectPool.setMinEvictableIdleTimeMillis(250L); - genericObjectPool.setTimeBetweenEvictionRunsMillis(500L); - genericObjectPool.setTestWhileIdle(true); + @Override + protected boolean isLifo() { + return true; + } - // ClassNotFoundException - try { - genericObjectPool.setEvictionPolicyClassName(Long.toString(System.currentTimeMillis())); - fail("setEvictionPolicyClassName must throw an error if the class name is invalid."); - } catch (final IllegalArgumentException e) { - // expected - } + @Override + protected ObjectPool<String> makeEmptyPool(final int mincap) { + final GenericObjectPool<String> mtPool = + new GenericObjectPool<>(new SimpleFactory()); + mtPool.setMaxTotal(mincap); + mtPool.setMaxIdle(mincap); + return mtPool; + } - // InstantiationException - try { - genericObjectPool.setEvictionPolicyClassName(java.io.Serializable.class.getName()); - fail("setEvictionPolicyClassName must throw an error if the class name is invalid."); - } catch (final IllegalArgumentException e) { - // expected - } + @Override + protected ObjectPool<Object> makeEmptyPool( + final PooledObjectFactory<Object> fac) { + return new GenericObjectPool<>(fac); + } - // IllegalAccessException - try { - genericObjectPool.setEvictionPolicyClassName(java.util.Collections.class.getName()); - fail("setEvictionPolicyClassName must throw an error if the class name is invalid."); - } catch (final IllegalArgumentException e) { - // expected + /** + * Kicks off <numThreads> test threads, each of which will go through + * <iterations> borrow-return cycles with random delay times <= delay + * in between. + */ + @SuppressWarnings({ + "rawtypes", "unchecked" + }) + private void runTestThreads(final int numThreads, final int iterations, final int delay, final GenericObjectPool testPool) { + final TestThread[] threads = new TestThread[numThreads]; + for(int i=0;i<numThreads;i++) { + threads[i] = new TestThread<String>(testPool,iterations,delay); + final Thread t = new Thread(threads[i]); + t.start(); } - - try { - genericObjectPool.setEvictionPolicyClassName(java.lang.String.class.getName()); - fail("setEvictionPolicyClassName must throw an error if a class that does not " - + "implement EvictionPolicy is specified."); - } catch (final IllegalArgumentException e) { - // expected + 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]._error.toString()); + } } + } - genericObjectPool.setEvictionPolicy(new TestEvictionPolicy<String>()); - assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName()); + @Before + public void setUp() throws Exception { + simpleFactory = new SimpleFactory(); + genericObjectPool = new GenericObjectPool<>(simpleFactory); + } - genericObjectPool.setEvictionPolicyClassName(TestEvictionPolicy.class.getName()); - assertEquals(TestEvictionPolicy.class.getName(), genericObjectPool.getEvictionPolicyClassName()); + @After + public void tearDown() throws Exception { + final String poolName = genericObjectPool.getJmxName().toString(); + genericObjectPool.clear(); + genericObjectPool.close(); + genericObjectPool = null; + simpleFactory = null; - final String[] active = new String[500]; - for(int i=0;i<500;i++) { - active[i] = genericObjectPool.borrowObject(); - } - for(int i=0;i<500;i++) { - genericObjectPool.returnObject(active[i]); + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final Set<ObjectName> result = mbs.queryNames(new ObjectName( + "org.apache.commoms.pool2:type=GenericObjectPool,*"), null); + // There should be no registered pools at this point + final int registeredPoolCount = result.size(); + final StringBuilder msg = new StringBuilder("Current pool is: "); + msg.append(poolName); + msg.append(" Still open pools are: "); + for (final ObjectName name : result) { + // Clean these up ready for the next test + msg.append(name.toString()); + msg.append(" created via\n"); + msg.append(mbs.getAttribute(name, "CreationStackTrace")); + msg.append('\n'); + mbs.unregisterMBean(name); } + Assert.assertEquals(msg.toString(), 0, registeredPoolCount); + } - // Eviction policy ignores first 1500 attempts to evict and then always - // evicts. After 1s, there should have been two runs of 500 tests so no - // evictions - try { Thread.sleep(1000L); } catch(final InterruptedException e) { } - assertEquals("Should be 500 idle", 500, genericObjectPool.getNumIdle()); - // A further 1s wasn't enough so allow 2s for the evictor to clear out - // all of the idle objects. - try { Thread.sleep(2000L); } catch(final InterruptedException e) { } - assertEquals("Should be 0 idle", 0, genericObjectPool.getNumIdle()); + @Test(timeout=60000) + public void testAddObject() throws Exception { + assertEquals("should be zero idle", 0, genericObjectPool.getNumIdle()); + genericObjectPool.addObject(); + assertEquals("should be one idle", 1, genericObjectPool.getNumIdle()); + assertEquals("should be zero active", 0, genericObjectPool.getNumActive()); + final String obj = genericObjectPool.borrowObject(); + assertEquals("should be zero idle", 0, genericObjectPool.getNumIdle()); + assertEquals("should be one active", 1, genericObjectPool.getNumActive()); + genericObjectPool.returnObject(obj); + assertEquals("should be one idle", 1, genericObjectPool.getNumIdle()); + assertEquals("should be zero active", 0, genericObjectPool.getNumActive()); } + /* + * Note: This test relies on timing for correct execution. There *should* be + * enough margin for this to work correctly on most (all?) systems but be + * aware of this if you see a failure of this test. + */ + @SuppressWarnings({ + "rawtypes", "unchecked" + }) @Test(timeout=60000) - public void testEvictionSoftMinIdle() throws Exception { - class TimeTest extends BasePooledObjectFactory<TimeTest> { - private final long createTime; + public void testBorrowObjectFairness() throws Exception { - public TimeTest() { - createTime = System.currentTimeMillis(); - } + final int numThreads = 40; + final int maxTotal = 40; - @Override - public TimeTest create() throws Exception { - return new TimeTest(); - } + final GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(maxTotal); + config.setMaxIdle(maxTotal); + config.setFairness(true); + config.setLifo(false); - @Override - public PooledObject<TimeTest> wrap(final TimeTest value) { - return new DefaultPooledObject<>(value); - } + genericObjectPool = new GenericObjectPool(simpleFactory, config); - public long getCreateTime() { - return createTime; - } + // Exhaust the pool + final String[] objects = new String[maxTotal]; + for (int i = 0; i < maxTotal; i++) { + objects[i] = genericObjectPool.borrowObject(); } - try (final GenericObjectPool<TimeTest> timePool = new GenericObjectPool<>(new TimeTest())) { + // Start and park threads waiting to borrow objects + final TestThread[] threads = new TestThread[numThreads]; + for(int i=0;i<numThreads;i++) { + threads[i] = new TestThread(genericObjectPool, 1, 0, 2000, false, String.valueOf(i % maxTotal)); + final Thread t = new Thread(threads[i]); + t.start(); + // Short delay to ensure threads start in correct order + try { + Thread.sleep(10); + } catch (final InterruptedException e) { + fail(e.toString()); + } + } - timePool.setMaxIdle(5); - timePool.setMaxTotal(5); - timePool.setNumTestsPerEvictionRun(5); - timePool.setMinEvictableIdleTimeMillis(3000L); - timePool.setSoftMinEvictableIdleTimeMillis(1000L); - timePool.setMinIdle(2); + // Return objects, other threads should get served in order + for (int i = 0; i < maxTotal; i++) { + genericObjectPool.returnObject(objects[i]); + } - final TimeTest[] active = new TimeTest[5]; - final Long[] creationTime = new Long[5]; - for (int i = 0; i < 5; i++) { - active[i] = timePool.borrowObject(); - creationTime[i] = Long.valueOf((active[i]).getCreateTime()); + // 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 + } } - - for (int i = 0; i < 5; i++) { - timePool.returnObject(active[i]); + if(threads[i].failed()) { + fail("Thread "+i+" failed: "+threads[i]._error.toString()); } - - // Soft evict all but minIdle(2) - Thread.sleep(1500L); - timePool.evict(); - assertEquals("Idle count different than expected.", 2, timePool.getNumIdle()); - - // Hard evict the rest. - Thread.sleep(2000L); - timePool.evict(); - assertEquals("Idle count different than expected.", 0, timePool.getNumIdle()); } } + /** + * On first borrow, first object fails validation, second object is OK. + * Subsequent borrows are OK. This was POOL-152. + */ @Test(timeout=60000) - public void testEvictionInvalid() throws Exception { + public void testBrokenFactoryShouldNotBlockPool() { + final int maxTotal = 1; - try (final GenericObjectPool<Object> invalidFactoryPool = new GenericObjectPool<>(new InvalidFactory())) { + simpleFactory.setMaxTotal(maxTotal); + genericObjectPool.setMaxTotal(maxTotal); + genericObjectPool.setBlockWhenExhausted(true); + genericObjectPool.setTestOnBorrow(true); - invalidFactoryPool.setMaxIdle(1); - invalidFactoryPool.setMaxTotal(1); - invalidFactoryPool.setTestOnBorrow(false); - invalidFactoryPool.setTestOnReturn(false); - invalidFactoryPool.setTestWhileIdle(true); - invalidFactoryPool.setMinEvictableIdleTimeMillis(100000); - invalidFactoryPool.setNumTestsPerEvictionRun(1); + // First borrow object will need to create a new object which will fail + // validation. + String obj = null; + Exception ex = null; + simpleFactory.setValid(false); + try { + obj = genericObjectPool.borrowObject(); + } catch (final Exception e) { + ex = e; + } + // Failure expected + assertNotNull(ex); + assertTrue(ex instanceof NoSuchElementException); + assertNull(obj); - final Object p = invalidFactoryPool.borrowObject(); - invalidFactoryPool.returnObject(p); + // Configure factory to create valid objects so subsequent borrows work + simpleFactory.setValid(true); - // Run eviction in a separate thread - final Thread t = new EvictionThread<>(invalidFactoryPool); - t.start(); + // Subsequent borrows should be OK + try { + obj = genericObjectPool.borrowObject(); + } catch (final Exception e1) { + fail(); + } + assertNotNull(obj); + try { + genericObjectPool.returnObject(obj); + <TRUNCATED>
