blob: af6ca60a57298458662becadd01948eda9c1b84b [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.pool2.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.Random;
import org.apache.commons.pool2.BaseKeyedPoolableObjectFactory;
import org.apache.commons.pool2.KeyedObjectPool;
import org.apache.commons.pool2.KeyedPoolableObjectFactory;
import org.apache.commons.pool2.TestBaseKeyedObjectPool;
import org.apache.commons.pool2.VisitTracker;
import org.apache.commons.pool2.VisitTrackerFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author Rodney Waldhoff
* @version $Revision$ $Date$
*/
public class TestGenericKeyedObjectPool extends TestBaseKeyedObjectPool {
@Override
protected KeyedObjectPool<Object,Object> makeEmptyPool(int mincapacity) {
GenericKeyedObjectPool <Object,Object>pool = new GenericKeyedObjectPool<Object,Object>(
new KeyedPoolableObjectFactory<Object,Object>() {
HashMap<Object,Integer> map = new HashMap<Object,Integer>();
public Object makeObject(Object key) {
int counter = 0;
Integer Counter = map.get(key);
if(null != Counter) {
counter = Counter.intValue();
}
map.put(key,new Integer(counter + 1));
return String.valueOf(key) + String.valueOf(counter);
}
public void destroyObject(Object key, Object obj) { }
public boolean validateObject(Object key, Object obj) { return true; }
public void activateObject(Object key, Object obj) { }
public void passivateObject(Object key, Object obj) { }
}
);
pool.setMaxTotalPerKey(mincapacity);
pool.setMaxIdlePerKey(mincapacity);
return pool;
}
@Override
protected KeyedObjectPool<Object,Object> makeEmptyPool(KeyedPoolableObjectFactory<Object,Object> factory) {
return new GenericKeyedObjectPool<Object,Object>(factory);
}
@Override
protected Object getNthObject(Object key, int n) {
return String.valueOf(key) + String.valueOf(n);
}
@Override
protected Object makeKey(int n) {
return String.valueOf(n);
}
private GenericKeyedObjectPool<String,String> pool = null;
private final Integer zero = new Integer(0);
private final Integer one = new Integer(1);
private final Integer two = new Integer(2);
@Before
public void setUp() {
pool = new GenericKeyedObjectPool<String,String>(new SimpleFactory<String>());
}
@After
@Override
public void tearDown() throws Exception {
super.tearDown();
pool.clear();
pool.close();
pool = null;
}
@Test
public void testNegativeMaxActive() throws Exception {
pool.setMaxTotalPerKey(-1);
pool.setWhenExhaustedAction(WhenExhaustedAction.FAIL);
String obj = pool.borrowObject("");
assertEquals("0",obj);
pool.returnObject("",obj);
}
@Test
public void testNumActiveNumIdle2() throws Exception {
assertEquals(0,pool.getNumActive());
assertEquals(0,pool.getNumIdle());
assertEquals(0,pool.getNumActive("A"));
assertEquals(0,pool.getNumIdle("A"));
assertEquals(0,pool.getNumActive("B"));
assertEquals(0,pool.getNumIdle("B"));
String objA0 = pool.borrowObject("A");
String objB0 = pool.borrowObject("B");
assertEquals(2,pool.getNumActive());
assertEquals(0,pool.getNumIdle());
assertEquals(1,pool.getNumActive("A"));
assertEquals(0,pool.getNumIdle("A"));
assertEquals(1,pool.getNumActive("B"));
assertEquals(0,pool.getNumIdle("B"));
String objA1 = pool.borrowObject("A");
String objB1 = pool.borrowObject("B");
assertEquals(4,pool.getNumActive());
assertEquals(0,pool.getNumIdle());
assertEquals(2,pool.getNumActive("A"));
assertEquals(0,pool.getNumIdle("A"));
assertEquals(2,pool.getNumActive("B"));
assertEquals(0,pool.getNumIdle("B"));
pool.returnObject("A",objA0);
pool.returnObject("B",objB0);
assertEquals(2,pool.getNumActive());
assertEquals(2,pool.getNumIdle());
assertEquals(1,pool.getNumActive("A"));
assertEquals(1,pool.getNumIdle("A"));
assertEquals(1,pool.getNumActive("B"));
assertEquals(1,pool.getNumIdle("B"));
pool.returnObject("A",objA1);
pool.returnObject("B",objB1);
assertEquals(0,pool.getNumActive());
assertEquals(4,pool.getNumIdle());
assertEquals(0,pool.getNumActive("A"));
assertEquals(2,pool.getNumIdle("A"));
assertEquals(0,pool.getNumActive("B"));
assertEquals(2,pool.getNumIdle("B"));
}
@Test
public void testMaxIdlePerKey() throws Exception {
pool.setMaxTotalPerKey(100);
pool.setMaxIdlePerKey(8);
String[] active = new String[100];
for(int i=0;i<100;i++) {
active[i] = pool.borrowObject("");
}
assertEquals(100,pool.getNumActive(""));
assertEquals(0,pool.getNumIdle(""));
for(int i=0;i<100;i++) {
pool.returnObject("",active[i]);
assertEquals(99 - i,pool.getNumActive(""));
assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle(""));
}
for(int i=0;i<100;i++) {
active[i] = pool.borrowObject("a");
}
assertEquals(100,pool.getNumActive("a"));
assertEquals(0,pool.getNumIdle("a"));
for(int i=0;i<100;i++) {
pool.returnObject("a",active[i]);
assertEquals(99 - i,pool.getNumActive("a"));
assertEquals((i < 8 ? i+1 : 8),pool.getNumIdle("a"));
}
// total number of idle instances is twice maxIdle
assertEquals(16, pool.getNumIdle());
// Each pool is at the sup
assertEquals(8, pool.getNumIdle(""));
assertEquals(8, pool.getNumIdle("a"));
}
@Test
public void testMaxActive() throws Exception {
pool.setMaxTotalPerKey(3);
pool.setWhenExhaustedAction(WhenExhaustedAction.FAIL);
pool.borrowObject("");
pool.borrowObject("");
pool.borrowObject("");
try {
pool.borrowObject("");
fail("Expected NoSuchElementException");
} catch(NoSuchElementException e) {
// expected
}
}
@Test
public void testMaxActiveZero() throws Exception {
pool.setMaxTotalPerKey(0);
pool.setWhenExhaustedAction(WhenExhaustedAction.FAIL);
try {
pool.borrowObject("a");
fail("Expected NoSuchElementException");
} catch(NoSuchElementException e) {
// expected
}
}
@Test
public void testWhenExhaustedGrow() throws Exception {
pool.setMaxTotalPerKey(1);
pool.setMaxTotal(1);
pool.setWhenExhaustedAction(WhenExhaustedAction.GROW);
for (int i = 0; i < 10; i++) {
pool.borrowObject("a");
}
}
@Test
public void testMaxTotal() throws Exception {
pool.setMaxTotalPerKey(2);
pool.setMaxTotal(3);
pool.setWhenExhaustedAction(WhenExhaustedAction.FAIL);
String o1 = pool.borrowObject("a");
assertNotNull(o1);
String o2 = pool.borrowObject("a");
assertNotNull(o2);
String o3 = pool.borrowObject("b");
assertNotNull(o3);
try {
pool.borrowObject("c");
fail("Expected NoSuchElementException");
} catch(NoSuchElementException e) {
// expected
}
assertEquals(0, pool.getNumIdle());
pool.returnObject("b", o3);
assertEquals(1, pool.getNumIdle());
assertEquals(1, pool.getNumIdle("b"));
Object o4 = pool.borrowObject("b");
assertNotNull(o4);
assertEquals(0, pool.getNumIdle());
assertEquals(0, pool.getNumIdle("b"));
pool.setMaxTotal(4);
Object o5 = pool.borrowObject("b");
assertNotNull(o5);
assertEquals(2, pool.getNumActive("a"));
assertEquals(2, pool.getNumActive("b"));
assertEquals(pool.getMaxTotal(),
pool.getNumActive("b") + pool.getNumActive("b"));
assertEquals(pool.getNumActive(),
pool.getMaxTotal());
}
@Test
public void testMaxTotalZero() throws Exception {
pool.setMaxTotal(0);
pool.setWhenExhaustedAction(WhenExhaustedAction.FAIL);
try {
pool.borrowObject("a");
fail("Expected NoSuchElementException");
} catch(NoSuchElementException e) {
// expected
}
}
@Test
public void testMaxTotalLRU() throws Exception {
pool.setMaxTotalPerKey(2);
pool.setMaxTotal(3);
// pool.setWhenExhaustedAction(GenericKeyedObjectPoolConfig.WHEN_EXHAUSTED_GROW);
String o1 = pool.borrowObject("a");
assertNotNull(o1);
pool.returnObject("a", o1);
Thread.sleep(25);
String o2 = pool.borrowObject("b");
assertNotNull(o2);
pool.returnObject("b", o2);
Thread.sleep(25);
String o3 = pool.borrowObject("c");
assertNotNull(o3);
pool.returnObject("c", o3);
Thread.sleep(25);
String o4 = pool.borrowObject("a");
assertNotNull(o4);
pool.returnObject("a", o4);
Thread.sleep(25);
assertSame(o1, o4);
// this should cause b to be bumped out of the pool
String o5 = pool.borrowObject("d");
assertNotNull(o5);
pool.returnObject("d", o5);
Thread.sleep(25);
// 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)
String o6 = pool.borrowObject("b");
assertNotNull(o6);
pool.returnObject("b", o6);
assertNotSame(o1, o6);
// second a is still in there
String o7 = pool.borrowObject("a");
assertNotNull(o7);
pool.returnObject("a", o7);
assertSame(o4, o7);
}
@Test
public void testSettersAndGetters() throws Exception {
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(new SimpleFactory<String>());
{
pool.setMaxTotalPerKey(123);
assertEquals(123,pool.getMaxTotalPerKey());
}
{
pool.setMaxIdlePerKey(12);
assertEquals(12,pool.getMaxIdlePerKey());
}
{
pool.setMaxWait(1234L);
assertEquals(1234L,pool.getMaxWait());
}
{
pool.setMinEvictableIdleTimeMillis(12345L);
assertEquals(12345L,pool.getMinEvictableIdleTimeMillis());
}
{
pool.setNumTestsPerEvictionRun(11);
assertEquals(11,pool.getNumTestsPerEvictionRun());
}
{
pool.setTestOnBorrow(true);
assertTrue(pool.getTestOnBorrow());
pool.setTestOnBorrow(false);
assertTrue(!pool.getTestOnBorrow());
}
{
pool.setTestOnReturn(true);
assertTrue(pool.getTestOnReturn());
pool.setTestOnReturn(false);
assertTrue(!pool.getTestOnReturn());
}
{
pool.setTestWhileIdle(true);
assertTrue(pool.getTestWhileIdle());
pool.setTestWhileIdle(false);
assertTrue(!pool.getTestWhileIdle());
}
{
pool.setTimeBetweenEvictionRunsMillis(11235L);
assertEquals(11235L,pool.getTimeBetweenEvictionRunsMillis());
}
{
pool.setWhenExhaustedAction(WhenExhaustedAction.BLOCK);
assertEquals(WhenExhaustedAction.BLOCK,pool.getWhenExhaustedAction());
pool.setWhenExhaustedAction(WhenExhaustedAction.FAIL);
assertEquals(WhenExhaustedAction.FAIL,pool.getWhenExhaustedAction());
pool.setWhenExhaustedAction(WhenExhaustedAction.GROW);
assertEquals(WhenExhaustedAction.GROW,pool.getWhenExhaustedAction());
}
}
@Test
public void testEviction() throws Exception {
pool.setMaxIdlePerKey(500);
pool.setMaxTotalPerKey(500);
pool.setNumTestsPerEvictionRun(100);
pool.setMinEvictableIdleTimeMillis(250L);
pool.setTimeBetweenEvictionRunsMillis(500L);
String[] active = new String[500];
for(int i=0;i<500;i++) {
active[i] = pool.borrowObject("");
}
for(int i=0;i<500;i++) {
pool.returnObject("",active[i]);
}
try { Thread.sleep(1000L); } catch(InterruptedException e) { }
assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 500);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 400);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 300);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 200);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 100);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertEquals("Should be zero idle, found " + pool.getNumIdle(""),0,pool.getNumIdle(""));
for(int i=0;i<500;i++) {
active[i] = pool.borrowObject("");
}
for(int i=0;i<500;i++) {
pool.returnObject("",active[i]);
}
try { Thread.sleep(1000L); } catch(InterruptedException e) { }
assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 500);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 400);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 300);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 200);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(""),pool.getNumIdle("") < 100);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertEquals("Should be zero idle, found " + pool.getNumIdle(""),0,pool.getNumIdle(""));
}
@Test
public void testEviction2() throws Exception {
pool.setMaxIdlePerKey(500);
pool.setMaxTotalPerKey(500);
pool.setNumTestsPerEvictionRun(100);
pool.setMinEvictableIdleTimeMillis(500L);
pool.setTimeBetweenEvictionRunsMillis(500L);
String[] active = new String[500];
String[] active2 = new String[500];
for(int i=0;i<500;i++) {
active[i] = pool.borrowObject("");
active2[i] = pool.borrowObject("2");
}
for(int i=0;i<500;i++) {
pool.returnObject("",active[i]);
pool.returnObject("2",active2[i]);
}
try { Thread.sleep(1100L); } catch(InterruptedException e) { }
assertTrue("Should be less than 1000 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 1000);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 900 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 900);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 800 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 800);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 700 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 700);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 600 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 600);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 500 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 500);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 400 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 400);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 300 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 300);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 200 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 200);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertTrue("Should be less than 100 idle, found " + pool.getNumIdle(),pool.getNumIdle() < 100);
try { Thread.sleep(600L); } catch(InterruptedException e) { }
assertEquals("Should be zero idle, found " + pool.getNumIdle(),0,pool.getNumIdle());
}
/**
* Kicks off <numThreads> test threads, each of which will go through
* <iterations> borrow-return cycles with random delay times <= delay
* in between.
*/
public void runTestThreads(int numThreads, int iterations, int delay) {
TestThread[] threads = new TestThread[numThreads];
for(int i=0;i<numThreads;i++) {
threads[i] = new TestThread(pool,iterations,delay);
Thread t = new Thread(threads[i]);
t.start();
}
for(int i=0;i<numThreads;i++) {
while(!(threads[i]).complete()) {
try {
Thread.sleep(500L);
} catch(InterruptedException e) {
// ignored
}
}
if(threads[i].failed()) {
fail("Thread failed: "+i+"\n"+getExceptionTrace(threads[i]._exception));
}
}
}
@Test
public void testThreaded1() throws Exception {
pool.setMaxTotalPerKey(15);
pool.setMaxIdlePerKey(15);
pool.setMaxWait(1000L);
runTestThreads(20, 100, 50);
}
/**
* Verifies that maxTotal is not exceeded when factory destroyObject
* has high latency, testOnReturn is set and there is high incidence of
* validation failures.
*/
@Test
public void testMaxTotalInvariant() throws Exception {
int maxTotal = 15;
SimpleFactory<String> factory = new SimpleFactory<String>();
factory.setEvenValid(false); // Every other validation fails
factory.setDestroyLatency(100); // Destroy takes 100 ms
factory.setMaxTotalPerKey(maxTotal); // (makes - destroys) bound
factory.setValidationEnabled(true);
pool = new GenericKeyedObjectPool<String,String>(factory);
pool.setMaxTotal(maxTotal);
pool.setMaxIdlePerKey(-1);
pool.setTestOnReturn(true);
pool.setMaxWait(10000L);
runTestThreads(5, 10, 50);
}
@Test
public void testMinIdle() throws Exception {
pool.setMaxIdlePerKey(500);
pool.setMinIdle(5);
pool.setMaxTotalPerKey(10);
pool.setNumTestsPerEvictionRun(0);
pool.setMinEvictableIdleTimeMillis(50L);
pool.setTimeBetweenEvictionRunsMillis(100L);
pool.setTestWhileIdle(true);
//Generate a random key
String key = "A";
pool.preparePool(key, true);
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
String[] active = new String[5];
active[0] = pool.borrowObject(key);
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
for(int i=1 ; i<5 ; i++) {
active[i] = pool.borrowObject(key);
}
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
for(int i=0 ; i<5 ; i++) {
pool.returnObject(key, active[i]);
}
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
}
@Test
public void testMinIdleMaxActive() throws Exception {
pool.setMaxIdlePerKey(500);
pool.setMinIdle(5);
pool.setMaxTotalPerKey(10);
pool.setNumTestsPerEvictionRun(0);
pool.setMinEvictableIdleTimeMillis(50L);
pool.setTimeBetweenEvictionRunsMillis(100L);
pool.setTestWhileIdle(true);
String key = "A";
pool.preparePool(key, true);
assertTrue("Should be 5 idle, found " +
pool.getNumIdle(),pool.getNumIdle() == 5);
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
String[] active = new String[10];
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
for(int i=0 ; i<5 ; i++) {
active[i] = pool.borrowObject(key);
}
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
for(int i=0 ; i<5 ; i++) {
pool.returnObject(key, active[i]);
}
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
for(int i=0 ; i<10 ; i++) {
active[i] = pool.borrowObject(key);
}
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
for(int i=0 ; i<10 ; i++) {
pool.returnObject(key, active[i]);
}
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 10 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 10);
}
@Test
public void testMinIdleNoPopulateImmediately() throws Exception {
pool.setMaxIdlePerKey(500);
pool.setMinIdle(5);
pool.setMaxTotalPerKey(10);
pool.setNumTestsPerEvictionRun(0);
pool.setMinEvictableIdleTimeMillis(50L);
pool.setTimeBetweenEvictionRunsMillis(1000L);
pool.setTestWhileIdle(true);
//Generate a random key
String key = "A";
pool.preparePool(key, false);
assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
try { Thread.sleep(1500L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
}
@Test
public void testMinIdleNoPreparePool() throws Exception {
pool.setMaxIdlePerKey(500);
pool.setMinIdle(5);
pool.setMaxTotalPerKey(10);
pool.setNumTestsPerEvictionRun(0);
pool.setMinEvictableIdleTimeMillis(50L);
pool.setTimeBetweenEvictionRunsMillis(100L);
pool.setTestWhileIdle(true);
//Generate a random key
String key = "A";
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 0 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 0);
Object active = pool.borrowObject(key);
assertNotNull(active);
try { Thread.sleep(150L); } catch(InterruptedException e) { }
assertTrue("Should be 5 idle, found " + pool.getNumIdle(),pool.getNumIdle() == 5);
}
@Test
public void testFIFO() throws Exception {
pool.setLifo(false);
final String key = "key";
pool.addObject(key); // "key0"
pool.addObject(key); // "key1"
pool.addObject(key); // "key2"
assertEquals("Oldest", "key0", pool.borrowObject(key));
assertEquals("Middle", "key1", pool.borrowObject(key));
assertEquals("Youngest", "key2", pool.borrowObject(key));
assertEquals("new-3", "key3", pool.borrowObject(key));
pool.returnObject(key, "r");
assertEquals("returned", "r", pool.borrowObject(key));
assertEquals("new-4", "key4", pool.borrowObject(key));
}
@Test
public void testLIFO() throws Exception {
pool.setLifo(true);
final String key = "key";
pool.addObject(key); // "key0"
pool.addObject(key); // "key1"
pool.addObject(key); // "key2"
assertEquals("Youngest", "key2", pool.borrowObject(key));
assertEquals("Middle", "key1", pool.borrowObject(key));
assertEquals("Oldest", "key0", pool.borrowObject(key));
assertEquals("new-3", "key3", pool.borrowObject(key));
pool.returnObject(key, "r");
assertEquals("returned", "r", pool.borrowObject(key));
assertEquals("new-4", "key4", pool.borrowObject(key));
}
/**
* Test to make sure evictor visits least recently used objects first,
* regardless of FIFO/LIFO
*
* JIRA: POOL-86
*/
@Test
public void testEvictionOrder() throws Exception {
checkEvictionOrder(false);
checkEvictionOrder(true);
}
private void checkEvictionOrder(boolean lifo) throws Exception {
SimpleFactory<Integer> factory = new SimpleFactory<Integer>();
GenericKeyedObjectPool<Integer,String> pool = new GenericKeyedObjectPool<Integer,String>(factory);
pool.setNumTestsPerEvictionRun(2);
pool.setMinEvictableIdleTimeMillis(100);
pool.setLifo(lifo);
for (int i = 0; i < 3; i ++) {
Integer key = new Integer(i);
for (int j = 0; j < 5; j++) {
pool.addObject(key);
}
}
// 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)
*/
pool.evict(); // Kill (0,0),(0,1)
assertEquals(3, pool.getNumIdle(zero));
String objZeroA = pool.borrowObject(zero);
assertTrue(lifo ? objZeroA.equals("04") : objZeroA.equals("02"));
assertEquals(2, pool.getNumIdle(zero));
String objZeroB = pool.borrowObject(zero);
assertTrue(objZeroB.equals("03"));
assertEquals(1, pool.getNumIdle(zero));
pool.evict(); // Kill remaining 0 survivor and (1,5)
assertEquals(0, pool.getNumIdle(zero));
assertEquals(4, pool.getNumIdle(one));
String objOneA = pool.borrowObject(one);
assertTrue(lifo ? objOneA.equals("19") : objOneA.equals("16"));
assertEquals(3, pool.getNumIdle(one));
String objOneB = pool.borrowObject(one);
assertTrue(lifo ? objOneB.equals("18") : objOneB.equals("17"));
assertEquals(2, pool.getNumIdle(one));
pool.evict(); // Kill remaining 1 survivors
assertEquals(0, pool.getNumIdle(one));
pool.evict(); // Kill (2,10), (2,11)
assertEquals(3, pool.getNumIdle(two));
String objTwoA = pool.borrowObject(two);
assertTrue(lifo ? objTwoA.equals("214") : objTwoA.equals("212"));
assertEquals(2, pool.getNumIdle(two));
pool.evict(); // All dead now
assertEquals(0, pool.getNumIdle(two));
pool.evict(); // Should do nothing - make sure no exception
// Currently 2 zero, 2 one and 1 two active. Return them
pool.returnObject(zero, objZeroA);
pool.returnObject(zero, objZeroB);
pool.returnObject(one, objOneA);
pool.returnObject(one, objOneB);
pool.returnObject(two, objTwoA);
// Remove all idle objects
pool.clear();
// Reload
pool.setMinEvictableIdleTimeMillis(500);
factory.counter = 0; // Reset counter
for (int i = 0; i < 3; i ++) {
Integer key = new Integer(i);
for (int j = 0; j < 5; j++) {
pool.addObject(key);
}
Thread.sleep(200);
}
// 0's are evictable, others not
pool.evict(); // Kill (0,0),(0,1)
assertEquals(3, pool.getNumIdle(zero));
pool.evict(); // Kill (0,2),(0,3)
assertEquals(1, pool.getNumIdle(zero));
pool.evict(); // Kill (0,4), leave (1,5)
assertEquals(0, pool.getNumIdle(zero));
assertEquals(5, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
pool.evict(); // (1,6), (1,7)
assertEquals(5, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
pool.evict(); // (1,8), (1,9)
assertEquals(5, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
pool.evict(); // (2,10), (2,11)
assertEquals(5, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
pool.evict(); // (2,12), (2,13)
assertEquals(5, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
pool.evict(); // (2,14), (1,5)
assertEquals(5, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
Thread.sleep(200); // Ones now timed out
pool.evict(); // kill (1,6), (1,7) - (1,5) missed
assertEquals(3, pool.getNumIdle(one));
assertEquals(5, pool.getNumIdle(two));
String obj = pool.borrowObject(one);
assertTrue(lifo ? obj.equals("19") : obj.equals("15"));
}
/**
* Verifies that the evictor visits objects in expected order
* and frequency.
*/
@Test
public void testEvictorVisiting() throws Exception {
checkEvictorVisiting(true);
checkEvictorVisiting(false);
}
private void checkEvictorVisiting(boolean lifo) throws Exception {
VisitTrackerFactory<Integer> factory = new VisitTrackerFactory<Integer>();
GenericKeyedObjectPool<Integer,VisitTracker<Integer>> pool = new GenericKeyedObjectPool<Integer,VisitTracker<Integer>>(factory);
pool.setNumTestsPerEvictionRun(2);
pool.setMinEvictableIdleTimeMillis(-1);
pool.setTestWhileIdle(true);
pool.setLifo(lifo);
pool.setTestOnReturn(false);
pool.setTestOnBorrow(false);
for (int i = 0; i < 3; i ++) {
factory.resetId();
Integer key = new Integer(i);
for (int j = 0; j < 8; j++) {
pool.addObject(key);
}
}
pool.evict(); // Visit oldest 2 - 00 and 01
VisitTracker<Integer> obj = pool.borrowObject(zero);
pool.returnObject(zero, obj);
obj = pool.borrowObject(zero);
pool.returnObject(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
pool.evict(); // Should visit 02 and 03 in either case
for (int i = 0; i < 8; i++) {
VisitTracker<Integer> tracker = pool.borrowObject(zero);
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());
}
}
// 0's are all out
pool.setNumTestsPerEvictionRun(3);
pool.evict(); // 10, 11, 12
pool.evict(); // 13, 14, 15
obj = pool.borrowObject(one);
pool.returnObject(one, obj);
obj = pool.borrowObject(one);
pool.returnObject(one, obj);
obj = pool.borrowObject(one);
pool.returnObject(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
pool.evict();
// LIFO - 16, 17, 20
// FIFO - 16, 17, 10
pool.evict();
// LIFO - 21, 22, 23
// FIFO - 11, 12, 20
pool.evict();
// LIFO - 24, 25, 26
// FIFO - 21, 22, 23
pool.evict();
// LIFO - 27, skip, 10
// FIFO - 24, 25, 26
for (int i = 0; i < 8; i++) {
VisitTracker<Integer> tracker = pool.borrowObject(one);
if ((lifo && tracker.getId() > 0) ||
(!lifo && tracker.getId() > 2)) {
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 some pools with random numTests
// and make sure evictor cycles through elements appropriately
int[] smallPrimes = {2, 3, 5, 7};
Random random = new Random();
random.setSeed(System.currentTimeMillis());
pool.setMaxIdlePerKey(-1);
for (int i = 0; i < smallPrimes.length; i++) {
pool.setNumTestsPerEvictionRun(smallPrimes[i]);
for (int j = 0; j < 5; j++) {// Try the tests a few times
pool.clear();
assertEquals("NumIdle should be zero after clearing the pool",0,pool.getNumIdle());
int zeroLength = 10 + random.nextInt(20);
for (int k = 0; k < zeroLength; k++) {
pool.addObject(zero);
}
int oneLength = 10 + random.nextInt(20);
for (int k = 0; k < oneLength; k++) {
pool.addObject(one);
}
int twoLength = 10 + random.nextInt(20);
for (int k = 0; k < twoLength; k++) {
pool.addObject(two);
}
// Choose a random number of evictor runs
int runs = 10 + random.nextInt(50);
for (int k = 0; k < runs; k++) {
pool.evict();
}
// Total instances in pool
int totalInstances = zeroLength + oneLength + twoLength;
// Number of times evictor should have cycled through pools
int cycleCount = (runs * pool.getNumTestsPerEvictionRun())
/ totalInstances;
// 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 = pool.borrowObject(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 = pool.borrowObject(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));
}
}
int visits[] = new int[twoLength];
for (int k = 0; k < twoLength; k++) {
tracker = pool.borrowObject(two);
visitCount = tracker.getValidateCount();
visits[k] = visitCount;
if (visitCount < cycleCount || visitCount > cycleCount + 1){
StringBuffer sb = new StringBuffer("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));
}
}
}
}
}
public void testConstructorsNoFactory() {
try {
new GenericKeyedObjectPool<Object,Object>(null);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
}
public void testConstructors() {
// Make constructor arguments all different from Builder.DEFAULTs
int maxActive = 1;
int maxIdle = 2;
long maxWait = 3;
int minIdle = 4;
int maxTotal = 5;
long minEvictableIdleTimeMillis = 6;
int numTestsPerEvictionRun = 7;
boolean testOnBorrow = true;
boolean testOnReturn = true;
boolean testWhileIdle = true;
long timeBetweenEvictionRunsMillis = 8;
WhenExhaustedAction whenExhaustedAction = WhenExhaustedAction.GROW;
boolean lifo = false;
KeyedPoolableObjectFactory<Object,Object> factory = new BaseKeyedPoolableObjectFactory<Object,Object>() {
@Override
public Object makeObject(Object key) throws Exception {
return null;
}
};
GenericKeyedObjectPool<Object,Object> pool = new GenericKeyedObjectPool<Object,Object>(factory);
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_IDLE_PER_KEY, pool.getMaxTotalPerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_IDLE_PER_KEY, pool.getMaxIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_WAIT, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
pool.getMinEvictableIdleTimeMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
pool.getNumTestsPerEvictionRun());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_BORROW,
pool.getTestOnBorrow());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_RETURN,
pool.getTestOnReturn());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_WHILE_IDLE,
pool.getTestWhileIdle());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_WHEN_EXHAUSTED_ACTION,
pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig.Builder()
.setLifo(lifo)
.setMaxTotalPerKey(maxActive)
.setMaxIdlePerKey(maxIdle)
.setMinIdlePerKey(minIdle)
.setMaxTotal(maxTotal)
.setMaxWait(maxWait)
.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis)
.setNumTestsPerEvictionRun(numTestsPerEvictionRun)
.setTestOnBorrow(testOnBorrow)
.setTestOnReturn(testOnReturn)
.setTestWhileIdle(testWhileIdle)
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis)
.setWhenExhaustedAction(whenExhaustedAction)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(minIdle, pool.getMinIdlePerKey());
assertEquals(maxTotal, pool.getMaxTotal());
assertEquals(minEvictableIdleTimeMillis,
pool.getMinEvictableIdleTimeMillis());
assertEquals(numTestsPerEvictionRun, pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow,pool.getTestOnBorrow());
assertEquals(testOnReturn,pool.getTestOnReturn());
assertEquals(testWhileIdle,pool.getTestWhileIdle());
assertEquals(timeBetweenEvictionRunsMillis,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(lifo, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_IDLE_PER_KEY, pool.getMaxIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_WAIT, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
pool.getMinEvictableIdleTimeMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
pool.getNumTestsPerEvictionRun());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_BORROW,
pool.getTestOnBorrow());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_RETURN,
pool.getTestOnReturn());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_WHILE_IDLE,
pool.getTestWhileIdle());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_WHEN_EXHAUSTED_ACTION,
pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_IDLE_PER_KEY, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
pool.getMinEvictableIdleTimeMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
pool.getNumTestsPerEvictionRun());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_BORROW,
pool.getTestOnBorrow());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_RETURN,
pool.getTestOnReturn());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_WHILE_IDLE,
pool.getTestWhileIdle());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setTestOnReturn(testOnReturn)
.setTestOnReturn(testOnReturn)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_IDLE_PER_KEY, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
pool.getMinEvictableIdleTimeMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow,pool.getTestOnBorrow());
assertEquals(testOnReturn,pool.getTestOnReturn());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_WHILE_IDLE,
pool.getTestWhileIdle());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setMinIdlePerKey(minIdle)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
pool.getMinEvictableIdleTimeMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
pool.getNumTestsPerEvictionRun());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_BORROW,
pool.getTestOnBorrow());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_ON_RETURN,
pool.getTestOnReturn());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_WHILE_IDLE,
pool.getTestWhileIdle());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setMaxIdlePerKey(maxIdle)
.setTestOnBorrow(testOnBorrow)
.setTestOnReturn(testOnReturn)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
pool.getMinEvictableIdleTimeMillis());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow, pool.getTestOnBorrow());
assertEquals(testOnReturn, pool.getTestOnReturn());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TEST_WHILE_IDLE,
pool.getTestWhileIdle());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setMaxIdlePerKey(maxIdle)
.setTestOnBorrow(testOnBorrow)
.setTestOnReturn(testOnReturn)
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis)
.setNumTestsPerEvictionRun(numTestsPerEvictionRun)
.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis)
.setTestWhileIdle(testWhileIdle)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MAX_TOTAL, pool.getMaxTotal());
assertEquals(minEvictableIdleTimeMillis,
pool.getMinEvictableIdleTimeMillis());
assertEquals(numTestsPerEvictionRun,
pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow, pool.getTestOnBorrow());
assertEquals(testOnReturn, pool.getTestOnReturn());
assertEquals(testWhileIdle,
pool.getTestWhileIdle());
assertEquals(timeBetweenEvictionRunsMillis,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setMaxIdlePerKey(maxIdle)
.setMaxTotal(maxTotal)
.setTestOnBorrow(testOnBorrow)
.setTestOnReturn(testOnReturn)
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis)
.setNumTestsPerEvictionRun(numTestsPerEvictionRun)
.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis)
.setTestWhileIdle(testWhileIdle)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_MIN_IDLE_PER_KEY, pool.getMinIdlePerKey());
assertEquals(maxTotal, pool.getMaxTotal());
assertEquals(minEvictableIdleTimeMillis,
pool.getMinEvictableIdleTimeMillis());
assertEquals(numTestsPerEvictionRun,
pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow, pool.getTestOnBorrow());
assertEquals(testOnReturn, pool.getTestOnReturn());
assertEquals(testWhileIdle,
pool.getTestWhileIdle());
assertEquals(timeBetweenEvictionRunsMillis,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setMaxIdlePerKey(maxIdle)
.setMaxTotal(maxTotal)
.setMinIdlePerKey(minIdle)
.setTestOnBorrow(testOnBorrow)
.setTestOnReturn(testOnReturn)
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis)
.setNumTestsPerEvictionRun(numTestsPerEvictionRun)
.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis)
.setTestWhileIdle(testWhileIdle)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(minIdle, pool.getMinIdlePerKey());
assertEquals(maxTotal, pool.getMaxTotal());
assertEquals(minEvictableIdleTimeMillis,
pool.getMinEvictableIdleTimeMillis());
assertEquals(numTestsPerEvictionRun,
pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow, pool.getTestOnBorrow());
assertEquals(testOnReturn, pool.getTestOnReturn());
assertEquals(testWhileIdle,
pool.getTestWhileIdle());
assertEquals(timeBetweenEvictionRunsMillis,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(GenericKeyedObjectPoolConfig.Builder.DEFAULT_LIFO, pool.getLifo());
config = new GenericKeyedObjectPoolConfig.Builder()
.setMaxTotalPerKey(maxActive)
.setWhenExhaustedAction(whenExhaustedAction)
.setMaxWait(maxWait)
.setMaxIdlePerKey(maxIdle)
.setMaxTotal(maxTotal)
.setMinIdlePerKey(minIdle)
.setTestOnBorrow(testOnBorrow)
.setTestOnReturn(testOnReturn)
.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis)
.setNumTestsPerEvictionRun(numTestsPerEvictionRun)
.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis)
.setTestWhileIdle(testWhileIdle)
.setLifo(lifo)
.createConfig();
pool = new GenericKeyedObjectPool<Object,Object>(factory, config);
assertEquals(maxActive, pool.getMaxTotalPerKey());
assertEquals(maxIdle, pool.getMaxIdlePerKey());
assertEquals(maxWait, pool.getMaxWait());
assertEquals(minIdle, pool.getMinIdlePerKey());
assertEquals(maxTotal, pool.getMaxTotal());
assertEquals(minEvictableIdleTimeMillis,
pool.getMinEvictableIdleTimeMillis());
assertEquals(numTestsPerEvictionRun,
pool.getNumTestsPerEvictionRun());
assertEquals(testOnBorrow, pool.getTestOnBorrow());
assertEquals(testOnReturn, pool.getTestOnReturn());
assertEquals(testWhileIdle,
pool.getTestWhileIdle());
assertEquals(timeBetweenEvictionRunsMillis,
pool.getTimeBetweenEvictionRunsMillis());
assertEquals(whenExhaustedAction,pool.getWhenExhaustedAction());
assertEquals(lifo, pool.getLifo());
}
@Test
public void testExceptionOnPassivateDuringReturn() throws Exception {
SimpleFactory<String> factory = new SimpleFactory<String>();
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(factory);
String obj = pool.borrowObject("one");
factory.setThrowExceptionOnPassivate(true);
pool.returnObject("one", obj);
assertEquals(0,pool.getNumIdle());
pool.close();
}
@Test
public void testExceptionOnDestroyDuringBorrow() throws Exception {
SimpleFactory<String> factory = new SimpleFactory<String>();
factory.setThrowExceptionOnDestroy(true);
factory.setValidationEnabled(true);
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(factory);
pool.setTestOnBorrow(true);
pool.borrowObject("one");
factory.setValid(false); // Make validation fail on next borrow attempt
try {
pool.borrowObject("one");
fail("Expecting NoSuchElementException");
} catch (NoSuchElementException ex) {
// expected
}
assertEquals(1, pool.getNumActive("one"));
assertEquals(0, pool.getNumIdle("one"));
assertEquals(1, pool.getNumActive());
assertEquals(0, pool.getNumIdle());
}
@Test
public void testExceptionOnDestroyDuringReturn() throws Exception {
SimpleFactory<String> factory = new SimpleFactory<String>();
factory.setThrowExceptionOnDestroy(true);
factory.setValidationEnabled(true);
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(factory);
pool.setTestOnReturn(true);
String obj1 = pool.borrowObject("one");
pool.borrowObject("one");
factory.setValid(false); // Make validation fail
pool.returnObject("one", obj1);
assertEquals(1, pool.getNumActive("one"));
assertEquals(0, pool.getNumIdle("one"));
assertEquals(1, pool.getNumActive());
assertEquals(0, pool.getNumIdle());
}
@Test
public void testExceptionOnActivateDuringBorrow() throws Exception {
SimpleFactory<String> factory = new SimpleFactory<String>();
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(factory);
String obj1 = pool.borrowObject("one");
String obj2 = pool.borrowObject("one");
pool.returnObject("one", obj1);
pool.returnObject("one", obj2);
factory.setThrowExceptionOnActivate(true);
factory.setEvenValid(false);
// Activation will now throw every other time
// First attempt throws, but loop continues and second succeeds
String obj = pool.borrowObject("one");
assertEquals(1, pool.getNumActive("one"));
assertEquals(0, pool.getNumIdle("one"));
assertEquals(1, pool.getNumActive());
assertEquals(0, pool.getNumIdle());
pool.returnObject("one", obj);
factory.setValid(false);
// Validation will now fail on activation when borrowObject returns
// an idle instance, and then when attempting to create a new instance
try {
pool.borrowObject("one");
fail("Expecting NoSuchElementException");
} catch (NoSuchElementException ex) {
// expected
}
assertEquals(0, pool.getNumActive("one"));
assertEquals(0, pool.getNumIdle("one"));
assertEquals(0, pool.getNumActive());
assertEquals(0, pool.getNumIdle());
}
public void testBlockedKeyDoesNotBlockPool() throws Exception {
SimpleFactory<String> factory = new SimpleFactory<String>();
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(factory);
pool.setWhenExhaustedAction(WhenExhaustedAction.BLOCK);
pool.setMaxWait(5000);
pool.setMaxTotalPerKey(1);
pool.setMaxTotal(-1);
pool.borrowObject("one");
long start = System.currentTimeMillis();
// Needs to be in a separate thread as this will block
Runnable simple = new SimpleTestThread(pool, "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);
pool.borrowObject("two");
long end = 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 ("Elapsed time: "+(end-start)+" should be less than 4000",(end-start) < 4000);
}
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 http://jira.codehaus.org/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
public void testMaxWaitMultiThreaded() throws Exception {
final long maxWait = 500; // wait for connection
final long holdTime = 2 * 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
SimpleFactory<String> factory = new SimpleFactory<String>();
GenericKeyedObjectPool<String,String> pool = new GenericKeyedObjectPool<String,String>(factory);
pool.setWhenExhaustedAction(WhenExhaustedAction.BLOCK);
pool.setMaxWait(maxWait);
pool.setMaxTotalPerKey(threadsPerKey);
// Create enough threads so half the threads will have to wait
WaitingTestThread wtt[] = new WaitingTestThread[keyCount * threadsPerKey * 2];
for(int i=0; i < wtt.length; i++){
wtt[i] = new WaitingTestThread(pool,Integer.toString(i % keyCount),holdTime);
}
long origin = System.currentTimeMillis()-1000;
for(int i=0; i < wtt.length; i++){
wtt[i].start();
}
int failed = 0;
for(int i=0; i < wtt.length; i++){
wtt[i].join();
if (wtt[i]._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(int i=0; i < wtt.length; i++){
WaitingTestThread wt = wtt[i];
System.out.println(
"Preborrow: "+(wt.preborrow-origin)
+ " Postborrow: "+(wt.postborrow != 0 ? wt.postborrow-origin : -1)
+ " BorrowTime: "+(wt.postborrow != 0 ? wt.postborrow-wt.preborrow : -1)
+ " PostReturn: "+(wt.postreturn != 0 ? wt.postreturn-origin : -1)
+ " Ended: "+(wt.ended-origin)
+ " Key: "+(wt._key)
+ " ObjId: "+wt.objectId
);
}
}
assertEquals("Expected half the threads to fail",wtt.length/2,failed);
}
/*
* 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 implements Runnable {
private final KeyedObjectPool<String,String> _pool;
private final String _key;
public SimpleTestThread(KeyedObjectPool<String,String> pool, String key) {
_pool = pool;
_key = key;
}
public void run() {
try {
String obj = _pool.borrowObject(_key);
_pool.returnObject(_key, obj);
} catch (Exception e) {
// Ignore
}
}
}
/*
* 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;
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(KeyedObjectPool<String,String> pool, String key, long pause) {
_pool = pool;
_key = key;
_pause = pause;
_thrown = null;
}
@Override
public void run() {
try {
preborrow = System.currentTimeMillis();
String obj = _pool.borrowObject(_key);
objectId=obj.toString();
postborrow = System.currentTimeMillis();
Thread.sleep(_pause);
_pool.returnObject(_key, obj);
postreturn = System.currentTimeMillis();
} catch (Exception e) {
_thrown = e;
} finally{
ended = System.currentTimeMillis();
}
}
}
static class TestThread implements Runnable {
private final java.util.Random _random = new java.util.Random();
// Thread config items
private final KeyedObjectPool<String,String> _pool;
private final int _iter;
private final int _delay;
private volatile boolean _complete = false;
private volatile boolean _failed = false;
private volatile Exception _exception;
public TestThread(KeyedObjectPool<String,String> pool) {
this(pool, 100, 50);
}
public TestThread(KeyedObjectPool<String,String> pool, int iter) {
this(pool, iter, 50);
}
public TestThread(KeyedObjectPool<String,String> pool, int iter, int delay) {
_pool = pool;
_iter = iter;
_delay = delay;
}
public boolean complete() {
return _complete;
}
public boolean failed() {
return _failed;
}
public void run() {
for(int i=0;i<_iter;i++) {
String key = String.valueOf(_random.nextInt(3));
try {
Thread.sleep(_random.nextInt(_delay));
} catch(InterruptedException e) {
// ignored
}
String obj = null;
try {
obj = _pool.borrowObject(key);
} catch(Exception e) {
_exception = e;
_failed = true;
_complete = true;
break;
}
try {
Thread.sleep(_random.nextInt(_delay));
} catch(InterruptedException e) {
// ignored
}
try {
_pool.returnObject(key,obj);
} catch(Exception e) {
_exception = e;
_failed = true;
_complete = true;
break;
}
}
_complete = true;
}
}
static class SimpleFactory<K> implements KeyedPoolableObjectFactory<K,String> {
public SimpleFactory() {
this(true);
}
public SimpleFactory(boolean valid) {
this.valid = valid;
}
public String makeObject(K key) {
synchronized(this) {
activeCount++;
if (activeCount > maxActive) {
throw new IllegalStateException(
"Too many active instances: " + activeCount);
}
}
return String.valueOf(key) + String.valueOf(counter++);
}
public void destroyObject(K key, String obj) throws Exception {
doWait(destroyLatency);
synchronized(this) {
activeCount--;
}
if (exceptionOnDestroy) {
throw new Exception();
}
}
public boolean validateObject(K key, String obj) {
if (enableValidation) {
return validateCounter++%2 == 0 ? evenValid : oddValid;
} else {
return valid;
}
}
public void activateObject(K key, String obj) throws Exception {
if (exceptionOnActivate) {
if (!(validateCounter++%2 == 0 ? evenValid : oddValid)) {
throw new Exception();
}
}
}
public void passivateObject(K key, String obj) throws Exception {
if (exceptionOnPassivate) {
throw new Exception();
}
}
public void setMaxTotalPerKey(int maxActive) {
this.maxActive = maxActive;
}
public void setDestroyLatency(long destroyLatency) {
this.destroyLatency = destroyLatency;
}
public void setValidationEnabled(boolean b) {
enableValidation = b;
}
void setEvenValid(boolean valid) {
evenValid = valid;
}
void setValid(boolean valid) {
evenValid = valid;
oddValid = valid;
}
public void setThrowExceptionOnActivate(boolean b) {
exceptionOnActivate = b;
}
public void setThrowExceptionOnDestroy(boolean b) {
exceptionOnDestroy = b;
}
public void setThrowExceptionOnPassivate(boolean b) {
exceptionOnPassivate = b;
}
int counter = 0;
boolean valid;
int activeCount = 0;
int validateCounter = 0;
boolean evenValid = true;
boolean oddValid = true;
boolean enableValidation = false;
long destroyLatency = 0;
int maxActive = Integer.MAX_VALUE;
boolean exceptionOnPassivate = false;
boolean exceptionOnActivate = false;
boolean exceptionOnDestroy = false;
private void doWait(long latency) {
try {
Thread.sleep(latency);
} catch (InterruptedException ex) {
// ignore
}
}
}
@Override
protected boolean isLifo() {
return true;
}
@Override
protected boolean isFifo() {
return false;
}
private String getExceptionTrace(Throwable t){
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
private String formatSettings(String title, String s, int i, String s0, boolean b0, String s1, int i1, String s2, int i2, String s3, int i3,
String s4, int i4, String s5, int i5, String s6, int i6, int zeroLength, int oneLength, int twoLength){
StringBuffer sb = new StringBuffer(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();
}
}