org.apache.commons.pool.impl.StackKeyedObjectPool Maven / Gradle / Ivy
/*
* 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.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Stack;
import org.apache.commons.pool.BaseKeyedObjectPool;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.PoolUtils;
/**
* A simple, Stack
-based KeyedObjectPool
implementation.
*
* Given a {@link KeyedPoolableObjectFactory}, this class will maintain
* a simple pool of instances. A finite number of "sleeping"
* or inactive instances is enforced, but when the pool is
* empty, new instances are created to support the new load.
* Hence this class places no limit on the number of "active"
* instances created by the pool, but is quite useful for
* re-using Object
s without introducing
* artificial limits.
*
*
* @param the type of keys in this pool
* @param the type of objects held in this pool
*
* @author Rodney Waldhoff
* @author Sandy McArthur
* @version $Revision: 1222710 $ $Date: 2011-12-23 10:58:12 -0500 (Fri, 23 Dec 2011) $
* @see Stack
* @since Pool 1.0
*/
public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool {
/**
* Create a new pool using no factory.
* Clients must first set the {@link #setFactory factory} or
* may populate the pool using {@link #returnObject returnObject}
* before they can be {@link #borrowObject borrowed}.
*
* @see #StackKeyedObjectPool(KeyedPoolableObjectFactory)
* @see #setFactory(KeyedPoolableObjectFactory)
*/
public StackKeyedObjectPool() {
this(null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY);
}
/**
* Create a new pool using no factory.
* Clients must first set the {@link #setFactory factory} or
* may populate the pool using {@link #returnObject returnObject}
* before they can be {@link #borrowObject borrowed}.
*
* @param max cap on the number of "sleeping" instances in the pool
* @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int)
* @see #setFactory(KeyedPoolableObjectFactory)
*/
public StackKeyedObjectPool(int max) {
this(null,max,DEFAULT_INIT_SLEEPING_CAPACITY);
}
/**
* Create a new pool using no factory.
* Clients must first set the {@link #setFactory factory} or
* may populate the pool using {@link #returnObject returnObject}
* before they can be {@link #borrowObject borrowed}.
*
* @param max cap on the number of "sleeping" instances in the pool
* @param init initial size of the pool (this specifies the size of the container,
* it does not cause the pool to be pre-populated.)
* @see #StackKeyedObjectPool(KeyedPoolableObjectFactory, int, int)
* @see #setFactory(KeyedPoolableObjectFactory)
*/
public StackKeyedObjectPool(int max, int init) {
this(null,max,init);
}
/**
* Create a new SimpleKeyedObjectPool
using
* the specified factory
to create new instances.
*
* @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
*/
public StackKeyedObjectPool(KeyedPoolableObjectFactory factory) {
this(factory,DEFAULT_MAX_SLEEPING);
}
/**
* Create a new SimpleKeyedObjectPool
using
* the specified factory
to create new instances.
* capping the number of "sleeping" instances to max
*
* @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
* @param max cap on the number of "sleeping" instances in the pool
*/
public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max) {
this(factory,max,DEFAULT_INIT_SLEEPING_CAPACITY);
}
/**
* Create a new SimpleKeyedObjectPool
using
* the specified factory
to create new instances.
* capping the number of "sleeping" instances to max
,
* and initially allocating a container capable of containing
* at least init
instances.
*
* @param factory the {@link KeyedPoolableObjectFactory} used to populate the pool
* @param max cap on the number of "sleeping" instances in the pool
* @param init initial size of the pool (this specifies the size of the container,
* it does not cause the pool to be pre-populated.)
*/
public StackKeyedObjectPool(KeyedPoolableObjectFactory factory, int max, int init) {
_factory = factory;
_maxSleeping = (max < 0 ? DEFAULT_MAX_SLEEPING : max);
_initSleepingCapacity = (init < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : init);
_pools = new HashMap>();
_activeCount = new HashMap();
}
/**
* Borrows an object with the given key. If there are no idle instances under the
* given key, a new one is created.
*
* @param key the pool key
* @return keyed poolable object instance
*/
@Override
public synchronized V borrowObject(K key) throws Exception {
assertOpen();
Stack stack = (_pools.get(key));
if(null == stack) {
stack = new Stack();
stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
_pools.put(key,stack);
}
V obj = null;
do {
boolean newlyMade = false;
if (!stack.empty()) {
obj = stack.pop();
_totIdle--;
} else {
if(null == _factory) {
throw new NoSuchElementException("pools without a factory cannot create new objects as needed.");
} else {
obj = _factory.makeObject(key);
newlyMade = true;
}
}
if (null != _factory && null != obj) {
try {
_factory.activateObject(key, obj);
if (!_factory.validateObject(key, obj)) {
throw new Exception("ValidateObject failed");
}
} catch (Throwable t) {
PoolUtils.checkRethrow(t);
try {
_factory.destroyObject(key,obj);
} catch (Throwable t2) {
PoolUtils.checkRethrow(t2);
// swallowed
} finally {
obj = null;
}
if (newlyMade) {
throw new NoSuchElementException(
"Could not create a validated object, cause: " +
t.getMessage());
}
}
}
} while (obj == null);
incrementActiveCount(key);
return obj;
}
/**
* Returns obj
to the pool under key
. If adding the
* returning instance to the pool results in {@link #_maxSleeping maxSleeping}
* exceeded for the given key, the oldest instance in the idle object pool
* is destroyed to make room for the returning instance.
*
* @param key the pool key
* @param obj returning instance
*/
@Override
public synchronized void returnObject(K key, V obj) throws Exception {
decrementActiveCount(key);
if (null != _factory) {
if (_factory.validateObject(key, obj)) {
try {
_factory.passivateObject(key, obj);
} catch (Exception ex) {
_factory.destroyObject(key, obj);
return;
}
} else {
return;
}
}
if (isClosed()) {
if (null != _factory) {
try {
_factory.destroyObject(key, obj);
} catch (Exception e) {
// swallowed
}
}
return;
}
Stack stack = _pools.get(key);
if(null == stack) {
stack = new Stack();
stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
_pools.put(key,stack);
}
final int stackSize = stack.size();
if (stackSize >= _maxSleeping) {
final V staleObj;
if (stackSize > 0) {
staleObj = stack.remove(0);
_totIdle--;
} else {
staleObj = obj;
}
if(null != _factory) {
try {
_factory.destroyObject(key, staleObj);
} catch (Exception e) {
// swallowed
}
}
}
stack.push(obj);
_totIdle++;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void invalidateObject(K key, V obj) throws Exception {
decrementActiveCount(key);
if(null != _factory) {
_factory.destroyObject(key,obj);
}
notifyAll(); // _totalActive has changed
}
/**
* Create an object using the {@link KeyedPoolableObjectFactory#makeObject factory},
* passivate it, and then placed in the idle object pool.
* addObject
is useful for "pre-loading" a pool with idle objects.
*
* @param key the key a new instance should be added to
* @throws Exception when {@link KeyedPoolableObjectFactory#makeObject} fails.
* @throws IllegalStateException when no {@link #setFactory factory} has been set or after {@link #close} has been called on this pool.
*/
@Override
public synchronized void addObject(K key) throws Exception {
assertOpen();
if (_factory == null) {
throw new IllegalStateException("Cannot add objects without a factory.");
}
V obj = _factory.makeObject(key);
try {
if (!_factory.validateObject(key, obj)) {
return;
}
} catch (Exception e) {
try {
_factory.destroyObject(key, obj);
} catch (Exception e2) {
// swallowed
}
return;
}
_factory.passivateObject(key, obj);
Stack stack = _pools.get(key);
if(null == stack) {
stack = new Stack();
stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
_pools.put(key,stack);
}
final int stackSize = stack.size();
if (stackSize >= _maxSleeping) {
final V staleObj;
if (stackSize > 0) {
staleObj = stack.remove(0);
_totIdle--;
} else {
staleObj = obj;
}
try {
_factory.destroyObject(key, staleObj);
} catch (Exception e) {
// Don't swallow destroying the newly created object.
if (obj == staleObj) {
throw e;
}
}
} else {
stack.push(obj);
_totIdle++;
}
}
/**
* Returns the total number of instances currently idle in this pool.
*
* @return the total number of instances currently idle in this pool
*/
@Override
public synchronized int getNumIdle() {
return _totIdle;
}
/**
* Returns the total number of instances current borrowed from this pool but not yet returned.
*
* @return the total number of instances currently borrowed from this pool
*/
@Override
public synchronized int getNumActive() {
return _totActive;
}
/**
* Returns the number of instances currently borrowed from but not yet returned
* to the pool corresponding to the given key
.
*
* @param key the key to query
* @return the number of instances corresponding to the given key
currently borrowed in this pool
*/
@Override
public synchronized int getNumActive(K key) {
return getActiveCount(key);
}
/**
* Returns the number of instances corresponding to the given key
currently idle in this pool.
*
* @param key the key to query
* @return the number of instances corresponding to the given key
currently idle in this pool
*/
@Override
public synchronized int getNumIdle(K key) {
try {
return(_pools.get(key)).size();
} catch(Exception e) {
return 0;
}
}
/**
* Clears the pool, removing all pooled instances.
*/
@Override
public synchronized void clear() {
Iterator it = _pools.keySet().iterator();
while(it.hasNext()) {
K key = it.next();
Stack stack = _pools.get(key);
destroyStack(key,stack);
}
_totIdle = 0;
_pools.clear();
_activeCount.clear();
}
/**
* Clears the specified pool, removing all pooled instances corresponding to the given key
.
*
* @param key the key to clear
*/
@Override
public synchronized void clear(K key) {
Stack stack = _pools.remove(key);
destroyStack(key,stack);
}
/**
* Destroys all instances in the stack and clears the stack.
*
* @param key key passed to factory when destroying instances
* @param stack stack to destroy
*/
private synchronized void destroyStack(K key, Stack stack) {
if(null == stack) {
return;
} else {
if(null != _factory) {
Iterator it = stack.iterator();
while(it.hasNext()) {
try {
_factory.destroyObject(key,it.next());
} catch(Exception e) {
// ignore error, keep destroying the rest
}
}
}
_totIdle -= stack.size();
_activeCount.remove(key);
stack.clear();
}
}
/**
* Returns a string representation of this StackKeyedObjectPool, including
* the number of pools, the keys and the size of each keyed pool.
*
* @return Keys and pool sizes
*/
@Override
public synchronized String toString() {
StringBuffer buf = new StringBuffer();
buf.append(getClass().getName());
buf.append(" contains ").append(_pools.size()).append(" distinct pools: ");
Iterator it = _pools.keySet().iterator();
while(it.hasNext()) {
K key = it.next();
buf.append(" |").append(key).append("|=");
Stack s = _pools.get(key);
buf.append(s.size());
}
return buf.toString();
}
/**
* Close this pool, and free any resources associated with it.
*
* Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking
* this method on a pool will cause them to throw an {@link IllegalStateException}.
*
*
* @throws Exception deprecated: implementations should silently fail if not all resources can be freed.
*/
@Override
public void close() throws Exception {
super.close();
clear();
}
/**
* Sets the {@link KeyedPoolableObjectFactory factory} the pool uses
* to create new instances.
* Trying to change the factory
after a pool has been used will frequently
* throw an {@link UnsupportedOperationException}.
*
* @param factory the {@link KeyedPoolableObjectFactory} used to manage object instances
* @throws IllegalStateException when the factory cannot be set at this time
* @deprecated to be removed in pool 2.0
*/
@Deprecated
@Override
public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException {
if(0 < getNumActive()) {
throw new IllegalStateException("Objects are already active");
} else {
clear();
_factory = factory;
}
}
/**
* @return the {@link KeyedPoolableObjectFactory} used by this pool to manage object instances.
* @since 1.5.5
*/
public synchronized KeyedPoolableObjectFactory getFactory() {
return _factory;
}
/**
* Returns the active instance count for the given key.
*
* @param key pool key
* @return active count
*/
private int getActiveCount(K key) {
try {
return _activeCount.get(key).intValue();
} catch(NoSuchElementException e) {
return 0;
} catch(NullPointerException e) {
return 0;
}
}
/**
* Increment the active count for the given key. Also
* increments the total active count.
*
* @param key pool key
*/
private void incrementActiveCount(K key) {
_totActive++;
Integer old = _activeCount.get(key);
if(null == old) {
_activeCount.put(key,new Integer(1));
} else {
_activeCount.put(key,new Integer(old.intValue() + 1));
}
}
/**
* Decrements the active count for the given key.
* Also decrements the total active count.
*
* @param key pool key
*/
private void decrementActiveCount(K key) {
_totActive--;
Integer active = _activeCount.get(key);
if(null == active) {
// do nothing, either null or zero is OK
} else if(active.intValue() <= 1) {
_activeCount.remove(key);
} else {
_activeCount.put(key, new Integer(active.intValue() - 1));
}
}
/**
* @return map of keyed pools
* @since 1.5.5
*/
public Map> getPools() {
return _pools;
}
/**
* @return the cap on the number of "sleeping" instances in each
pool.
* @since 1.5.5
*/
public int getMaxSleeping() {
return _maxSleeping;
}
/**
* @return the initial capacity of each pool.
* @since 1.5.5
*/
public int getInitSleepingCapacity() {
return _initSleepingCapacity;
}
/**
* @return the _totActive
*/
public int getTotActive() {
return _totActive;
}
/**
* @return the _totIdle
*/
public int getTotIdle() {
return _totIdle;
}
/**
* @return the _activeCount
* @since 1.5.5
*/
public Map getActiveCount() {
return _activeCount;
}
/** The default cap on the number of "sleeping" instances in the pool. */
protected static final int DEFAULT_MAX_SLEEPING = 8;
/**
* The default initial size of the pool
* (this specifies the size of the container, it does not
* cause the pool to be pre-populated.)
*/
protected static final int DEFAULT_INIT_SLEEPING_CAPACITY = 4;
/**
* My named-set of pools.
* @deprecated to be removed in pool 2.0. Use {@link #getPools()}
*/
@Deprecated
protected HashMap> _pools = null;
/**
* My {@link KeyedPoolableObjectFactory}.
* @deprecated to be removed in pool 2.0. Use {@link #getFactory()}
*/
@Deprecated
protected KeyedPoolableObjectFactory _factory = null;
/**
* The cap on the number of "sleeping" instances in each
pool.
* @deprecated to be removed in pool 2.0. Use {@link #getMaxSleeping()}
*/
@Deprecated
protected int _maxSleeping = DEFAULT_MAX_SLEEPING;
/**
* The initial capacity of each pool.
* @deprecated to be removed in pool 2.0. Use {@link #getInitSleepingCapacity()}.
*/
@Deprecated
protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY;
/**
* Total number of object borrowed and not yet returned for all pools.
* @deprecated to be removed in pool 2.0. Use {@link #getTotActive()}.
*/
@Deprecated
protected int _totActive = 0;
/**
* Total number of objects "sleeping" for all pools
* @deprecated to be removed in pool 2.0. Use {@link #getTotIdle()}.
*/
@Deprecated
protected int _totIdle = 0;
/**
* Number of active objects borrowed and not yet returned by pool
* @deprecated to be removed in pool 2.0. Use {@link #getActiveCount()}.
*/
@Deprecated
protected HashMap _activeCount = null;
}