/*
* 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.pool.impl;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.TestBaseObjectPool;
/**
* @author Rodney Waldhoff
* @author Dirk Verbeeck
* @author Sandy McArthur
* @version $Revision: 1222396 $ $Date: 2011-12-22 14:02:25 -0500 (Thu, 22 Dec 2011) $
*/
public class TestStackObjectPool extends TestBaseObjectPool<String> {
public TestStackObjectPool(String testName) {
super(testName);
}
@Override
protected ObjectPool<String> makeEmptyPool(int mincap) {
return new StackObjectPool<String>(new SimpleFactory());
}
@Override
protected ObjectPool<Integer> makeEmptyPool(final PoolableObjectFactory<Integer> factory) {
return new StackObjectPool<Integer>(factory);
}
@Override
protected String getNthObject(int n) {
return String.valueOf(n);
}
public void testIdleCap() throws Exception {
ObjectPool<String> pool = makeEmptyPool(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());
}
}
/**
* @deprecated - to be removed in pool 2.0
*/
@Deprecated
public void testPoolWithNullFactory() throws Exception {
ObjectPool<Integer> pool = new StackObjectPool<Integer>(10);
for(int i=0;i<10;i++) {
pool.returnObject(new Integer(i));
}
for(int j=0;j<3;j++) {
Integer[] borrowed = new Integer[10];
BitSet found = new BitSet();
for(int i=0;i<10;i++) {
borrowed[i] = pool.borrowObject();
assertNotNull(borrowed);
assertTrue(!found.get(borrowed[i].intValue()));
found.set(borrowed[i].intValue());
}
for(int i=0;i<10;i++) {
pool.returnObject(borrowed[i]);
}
}
pool.invalidateObject(pool.borrowObject());
pool.invalidateObject(pool.borrowObject());
pool.clear();
}
/**
* @deprecated - to be removed in pool 2.0
*/
@Deprecated
public void testBorrowFromEmptyPoolWithNullFactory() throws Exception {
ObjectPool<Object> pool = new StackObjectPool<Object>();
try {
pool.borrowObject();
fail("Expected NoSuchElementException");
} catch(NoSuchElementException e) {
// expected
}
}
/**
* @deprecated - to be removed in pool 2.0
*/
@Deprecated
@Override
public void testSetFactory() throws Exception {
ObjectPool<String> pool = new StackObjectPool<String>();
try {
pool.borrowObject();
fail("Expected NoSuchElementException");
} catch(NoSuchElementException e) {
// expected
}
pool.setFactory(new SimpleFactory());
String obj = pool.borrowObject();
assertNotNull(obj);
pool.returnObject(obj);
}
/**
* @deprecated - to be removed in pool 2.0
*/
@Deprecated
public void testCantResetFactoryWithActiveObjects() throws Exception {
ObjectPool<String> pool = new StackObjectPool<String>();
pool.setFactory(new SimpleFactory());
String obj = pool.borrowObject();
assertNotNull(obj);
try {
pool.setFactory(new SimpleFactory());
fail("Expected IllegalStateException");
} catch(IllegalStateException e) {
// expected
}
}
/**
* @deprecated - to be removed in pool 2.0
*/
@Deprecated
public void testCanResetFactoryWithoutActiveObjects() throws Exception {
ObjectPool<String> pool = new StackObjectPool<String>();
{
pool.setFactory(new SimpleFactory());
String obj = pool.borrowObject();
assertNotNull(obj);
pool.returnObject(obj);
}
{
pool.setFactory(new SimpleFactory());
String obj = pool.borrowObject();
assertNotNull(obj);
pool.returnObject(obj);
}
}
/**
* Verifies that validation failures when borrowing newly created instances
* from the pool result in NoSuchElementExceptions and passivation failures
* result in instances not being returned to the pool.
*/
public void testBorrowWithSometimesInvalidObjects() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
factory.setValidateSelectively(true); // Even numbers fail validation
factory.setPassivateSelectively(true); // Multiples of 3 fail passivation
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 20);
Integer[] obj = new Integer[10];
for(int i=0;i<10;i++) {
Integer object = null;
int k = 0;
while (object == null && k < 100) { // bound not really needed
try {
k++;
object = pool.borrowObject();
if (object.intValue() % 2 == 0) {
fail("Expecting NoSuchElementException");
} else {
obj[i] = object;
}
} catch (NoSuchElementException ex) {
// Should fail for evens
}
}
assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
}
// 1,3,5,...,19 pass validation, get checked out
for(int i=0;i<10;i++) {
pool.returnObject(obj[i]);
assertEquals("Each time we return, get one less active.", 9-i, pool.getNumActive());
}
// 3, 9, 15 fail passivation.
assertEquals(7,pool.getNumIdle());
assertEquals(new Integer(19), pool.borrowObject());
assertEquals(new Integer(17), pool.borrowObject());
assertEquals(new Integer(13), pool.borrowObject());
assertEquals(new Integer(11), pool.borrowObject());
assertEquals(new Integer(7), pool.borrowObject());
assertEquals(new Integer(5), pool.borrowObject());
assertEquals(new Integer(1), pool.borrowObject());
}
/**
* Verifies that validation and passivation failures returning objects are handled
* properly - instances destroyed and not returned to the pool, but no exceptions propagated.
*/
public void testBorrowReturnWithSometimesInvalidObjects() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 20);
Integer[] obj = new Integer[10];
for(int i=0;i<10;i++) {
obj[i] = pool.borrowObject();
assertEquals("Each time we borrow, get one more active.", i+1, pool.getNumActive());
}
factory.setValidateSelectively(true); // Even numbers fail validation
factory.setPassivateSelectively(true); // Multiples of 3 fail passivation
for(int i=0;i<10;i++) {
pool.returnObject(obj[i]);
assertEquals("Each time we return, get one less active.", 9-i, pool.getNumActive());
}
// 0,2,4,6,8 fail validation, 3, 9 fail passivation - 3 left.
assertEquals(3,pool.getNumIdle());
}
public void testVariousConstructors() throws Exception {
{
StackObjectPool<Object> pool = new StackObjectPool<Object>();
assertNotNull(pool);
}
{
StackObjectPool<Object> pool = new StackObjectPool<Object>(10);
assertNotNull(pool);
}
{
StackObjectPool<Object> pool = new StackObjectPool<Object>(10,5);
assertNotNull(pool);
}
{
StackObjectPool<Object> pool = new StackObjectPool<Object>(null);
assertNotNull(pool);
}
{
StackObjectPool<Object> pool = new StackObjectPool<Object>(null,10);
assertNotNull(pool);
}
{
StackObjectPool<Object> pool = new StackObjectPool<Object>(null,10,5);
assertNotNull(pool);
}
}
/**
* Verify that out of range constructor arguments are ignored.
*/
public void testMaxIdleInitCapacityOutOfRange() throws Exception {
SimpleFactory factory = new SimpleFactory();
StackObjectPool<String> pool = new StackObjectPool<String>(factory, -1, 0);
assertEquals(pool.getMaxSleeping(), StackObjectPool.DEFAULT_MAX_SLEEPING);
pool.addObject();
pool.close();
}
/**
* Verifies that when returning objects cause maxSleeping exceeded, oldest instances
* are destroyed to make room for returning objects.
*/
public void testReturnObjectDiscardOrder() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 3);
// borrow more objects than the pool can hold
Integer i0 = pool.borrowObject();
Integer i1 = pool.borrowObject();
Integer i2 = pool.borrowObject();
Integer i3 = pool.borrowObject();
// tests
// return as many as the pool will hold.
pool.returnObject(i0);
pool.returnObject(i1);
pool.returnObject(i2);
// the pool should now be full.
assertEquals("No returned objects should have been destroyed yet.", 0, factory.getDestroyed().size());
// cause the pool to discard a stale object.
pool.returnObject(i3);
assertEquals("One object should have been destroyed.", 1, factory.getDestroyed().size());
// check to see what object was destroyed
Integer d = factory.getDestroyed().get(0);
assertEquals("Destoryed object should be the stalest object.", i0, d);
}
/**
* Verifies that exceptions thrown by factory activate method are not propagated to
* the caller. Objects that throw on activate are destroyed and if none succeed,
* the caller gets NoSuchElementException.
*/
public void testExceptionOnActivate() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
pool.addObject();
pool.addObject();
factory.setThrowOnActivate(true);
try {
pool.borrowObject();
fail("Expecting NoSuchElementException");
} catch (NoSuchElementException ex) {
// expected
}
assertEquals(0, pool.getNumIdle());
assertEquals(0, pool.getNumActive());
}
/**
* Verifies that exceptions thrown by factory destroy are swallowed
* by both addObject and returnObject.
*/
public void testExceptionOnDestroy() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 2);
factory.setThrowOnDestroy(true);
for (int i = 0; i < 3; i++) {
pool.addObject(); // Third one will destroy, exception should be swallowed
}
assertEquals(2, pool.getNumIdle());
Integer[] objects = new Integer[3];
for (int i = 0; i < 3; i++) {
objects[i] = pool.borrowObject();
}
for (int i = 0; i < 3; i++) {
pool.returnObject(objects[i]); // Third triggers destroy
}
assertEquals(2, pool.getNumIdle());
}
/**
* Verifies that addObject propagates exceptions thrown by
* factory passivate, but returnObject swallows these.
*/
public void testExceptionOnPassivate() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 2);
factory.setThrowOnPassivate(true);
// addObject propagates
try {
pool.addObject();
fail("Expecting IntegerFactoryException");
} catch (IntegerFactoryException ex) {
assertEquals("passivateObject", ex.getType());
assertEquals(0, ex.getValue());
}
assertEquals(0, pool.getNumIdle());
// returnObject swallows
Integer obj = pool.borrowObject();
pool.returnObject(obj);
assertEquals(0, pool.getNumIdle());
}
/**
* Verifies that validation exceptions always propagate
*/
public void testExceptionOnValidate() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory, 2);
factory.setThrowOnValidate(true);
// addObject
try {
pool.addObject();
fail("Expecting IntegerFactoryException");
} catch (IntegerFactoryException ex) {
assertEquals("validateObject", ex.getType());
}
assertEquals(0, pool.getNumIdle());
// returnObject
factory.setThrowOnValidate(false);
Integer obj = pool.borrowObject();
factory.setThrowOnValidate(true);
try {
pool.returnObject(obj);
fail("Expecting IntegerFactoryException");
} catch (IntegerFactoryException ex) {
assertEquals("validateObject", ex.getType());
}
assertEquals(0, pool.getNumIdle());
// borrowObject - throws NoSuchElementException
try {
pool.borrowObject();
fail("Expecting NoSuchElementException");
} catch (NoSuchElementException ex) {
// Expected
}
}
/**
* Verifies that exceptions thrown by makeObject are propagated.
*/
public void testExceptionOnMake() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
factory.setThrowOnMake(true);
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
try {
pool.borrowObject();
fail("Expecting IntegerFactoryException");
} catch (IntegerFactoryException ex) {
assertEquals("makeObject", ex.getType());
}
try {
pool.addObject();
fail("Expecting IntegerFactoryException");
} catch (IntegerFactoryException ex) {
assertEquals("makeObject", ex.getType());
}
}
/**
* Verifies NoSuchElementException when the factory returns a null object in borrowObject
*/
public void testMakeNull() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
factory.setMakeNull(true);
try {
pool.borrowObject();
fail("Expecting NoSuchElementException");
} catch (NoSuchElementException ex) {
// Expected
}
}
/**
* Verifies that initIdleCapacity is not a hard limit, but maxIdle is.
*/
public void testInitIdleCapacityExceeded() throws Exception {
PoolableObjectFactory<String> factory = new SimpleFactory();
ObjectPool<String> pool = new StackObjectPool<String>(factory, 2, 1);
pool.addObject();
pool.addObject();
assertEquals(2, pool.getNumIdle());
pool.close();
pool = new StackObjectPool<String>(factory, 1, 2);
pool.addObject();
pool.addObject();
assertEquals(1, pool.getNumIdle());
}
/**
* Verifies close contract - idle instances are destroyed, returning instances
* are destroyed, add/borrowObject throw IllegalStateException.
*/
@Override
public void testClose() throws Exception {
SelectiveFactory factory = new SelectiveFactory();
ObjectPool<Integer> pool = new StackObjectPool<Integer>(factory);
pool.addObject(); // 0
pool.addObject(); // 1
pool.addObject(); // 2
Integer two = pool.borrowObject();
assertEquals(2, two.intValue());
pool.close();
assertEquals(0, pool.getNumIdle());
assertEquals(1, pool.getNumActive());
List<Integer> destroyed = factory.getDestroyed();
assertEquals(2, destroyed.size());
assertTrue(destroyed.contains(new Integer(0)));
assertTrue(destroyed.contains(new Integer(0)));
pool.returnObject(two);
assertTrue(destroyed.contains(two));
try {
pool.addObject();
fail("Expecting IllegalStateException");
} catch (IllegalStateException ex) {
// Expected
}
try {
pool.borrowObject();
fail("Expecting IllegalStateException");
} catch (IllegalStateException ex) {
// Expected
}
}
/**
* Simple factory that creates Integers. Validation and other factory methods
* always succeed.
*/
static class SimpleFactory implements PoolableObjectFactory<String> {
int counter = 0;
public String makeObject() { return String.valueOf(counter++); }
public void destroyObject(String obj) { }
public boolean validateObject(String obj) { return true; }
public void activateObject(String obj) { }
public void passivateObject(String obj) { }
}
/**
* Integer factory that fails validation and other factory methods "selectively" and
* tracks object destruction.
*/
static class SelectiveFactory implements PoolableObjectFactory<Integer> {
private List<Integer> destroyed = new ArrayList<Integer>();
private int counter = 0;
private boolean validateSelectively = false; // true <-> validate returns false for even Integers
private boolean passivateSelectively = false; // true <-> passivate throws RTE if Integer = 0 mod 3
private boolean throwOnDestroy = false; // true <-> destroy throws RTE (always)
private boolean throwOnActivate = false; // true <-> activate throws RTE (always)
private boolean throwOnMake = false; // true <-> make throws RTE (always)
private boolean throwOnValidate= false; // true <-> validate throws RTE (always)
private boolean throwOnPassivate = false; // true <-> passivate throws RTE (always)
private boolean makeNull = false; // true <-> make returns null
public Integer makeObject() {
if (throwOnMake) {
final int next = counter + 1;
throw new IntegerFactoryException("makeObject", next);
} else {
return makeNull? null : new Integer(counter++);
}
}
public void destroyObject(Integer obj) {
if (throwOnDestroy) {
final Integer integer = obj;
throw new IntegerFactoryException("destroyObject", integer.intValue());
}
destroyed.add(obj);
}
public boolean validateObject(Integer obj) {
if (throwOnValidate) {
final Integer integer = obj;
throw new IntegerFactoryException("validateObject", integer.intValue());
}
if (validateSelectively) {
// only odd objects are valid
return ((obj.intValue() % 2) == 1);
}
return true;
}
public void activateObject(Integer obj) {
if (throwOnActivate) {
final Integer integer = obj;
throw new IntegerFactoryException("activateObject", integer.intValue());
}
}
public void passivateObject(Integer obj) {
if (throwOnPassivate) {
final Integer integer = obj;
throw new IntegerFactoryException("passivateObject", integer.intValue());
}
if (passivateSelectively) {
final Integer integer = obj;
if (integer.intValue() % 3 == 0) {
throw new IntegerFactoryException("passivateObject", integer.intValue());
}
}
}
public List<Integer> getDestroyed() {
return destroyed;
}
public void setCounter(int counter) {
this.counter = counter;
}
public void setValidateSelectively(boolean validateSelectively) {
this.validateSelectively = validateSelectively;
}
public void setPassivateSelectively(boolean passivateSelectively) {
this.passivateSelectively = passivateSelectively;
}
public void setThrowOnDestroy(boolean throwOnDestroy) {
this.throwOnDestroy = throwOnDestroy;
}
public void setThrowOnActivate(boolean throwOnActivate) {
this.throwOnActivate = throwOnActivate;
}
public void setThrowOnMake(boolean throwOnMake) {
this.throwOnMake = throwOnMake;
}
public void setThrowOnPassivate(boolean throwOnPassivate) {
this.throwOnPassivate = throwOnPassivate;
}
public void setThrowOnValidate(boolean throwOnValidate) {
this.throwOnValidate = throwOnValidate;
}
public void setMakeNull(boolean makeNull) {
this.makeNull = makeNull;
}
}
static class IntegerFactoryException extends RuntimeException {
private String type;
private int value;
public IntegerFactoryException(String type, int value) {
super(type + " failed. Value: " + value);
this.type = type;
this.value = value;
}
public String getType() {
return type;
}
public int getValue() {
return value;
}
}
@Override
protected boolean isLifo() {
return true;
}
@Override
protected boolean isFifo() {
return false;
}
}