/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed 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 com.frameworkset.commons.pool.impl; import java.util.HashMap; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Stack; import com.frameworkset.commons.pool.BaseKeyedObjectPool; import com.frameworkset.commons.pool.KeyedObjectPool; import com.frameworkset.commons.pool.KeyedPoolableObjectFactory; /** * A simple, {@link java.util.Stack Stack}-based {@link KeyedObjectPool} implementation. * <p> * 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 <tt>Object</tt>s without introducing * artificial limits. * * @author Rodney Waldhoff * @version $Revision: 328937 $ $Date: 2005-10-27 15:23:53 -0400 (Thu, 27 Oct 2005) $ */ public class StackKeyedObjectPool extends BaseKeyedObjectPool implements KeyedObjectPool { /** * Create a new pool using * no factory. Clients must first populate the pool * using {@link #returnObject(java.lang.Object,java.lang.Object)} * before they can be {@link #borrowObject(java.lang.Object) borrowed}. */ public StackKeyedObjectPool() { this((KeyedPoolableObjectFactory)null,DEFAULT_MAX_SLEEPING,DEFAULT_INIT_SLEEPING_CAPACITY); } /** * Create a new pool using * no factory. Clients must first populate the pool * using {@link #returnObject(java.lang.Object,java.lang.Object)} * before they can be {@link #borrowObject(java.lang.Object) borrowed}. * * @param max cap on the number of "sleeping" instances in the pool */ public StackKeyedObjectPool(int max) { this((KeyedPoolableObjectFactory)null,max,DEFAULT_INIT_SLEEPING_CAPACITY); } /** * Create a new pool using * no factory. Clients must first populate the pool * using {@link #returnObject(java.lang.Object,java.lang.Object)} * before they can be {@link #borrowObject(java.lang.Object) 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.) */ public StackKeyedObjectPool(int max, int init) { this((KeyedPoolableObjectFactory)null,max,init); } /** * Create a new <tt>SimpleKeyedObjectPool</tt> using * the specified <i>factory</i> 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 <tt>SimpleKeyedObjectPool</tt> using * the specified <i>factory</i> to create new instances. * capping the number of "sleeping" instances to <i>max</i> * * @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 <tt>SimpleKeyedObjectPool</tt> using * the specified <i>factory</i> to create new instances. * capping the number of "sleeping" instances to <i>max</i>, * and initially allocating a container capable of containing * at least <i>init</i> 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(); } public synchronized Object borrowObject(Object key) throws Exception { Object obj = null; Stack stack = (Stack)(_pools.get(key)); if(null == stack) { stack = new Stack(); stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); _pools.put(key,stack); } try { obj = stack.pop(); _totIdle--; } catch(Exception e) { if(null == _factory) { throw new NoSuchElementException(); } else { obj = _factory.makeObject(key); } } if(null != obj && null != _factory) { _factory.activateObject(key,obj); } incrementActiveCount(key); return obj; } public synchronized void returnObject(Object key, Object obj) throws Exception { decrementActiveCount(key); if(null == _factory || _factory.validateObject(key,obj)) { Stack stack = (Stack)(_pools.get(key)); if(null == stack) { stack = new Stack(); stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity); _pools.put(key,stack); } if(null != _factory) { try { _factory.passivateObject(key,obj); } catch(Exception e) { _factory.destroyObject(key,obj); return; } } if(stack.size() < _maxSleeping) { stack.push(obj); _totIdle++; } else { if(null != _factory) { _factory.destroyObject(key,obj); } } } else { if(null != _factory) { _factory.destroyObject(key,obj); } } } public synchronized void invalidateObject(Object key, Object obj) throws Exception { decrementActiveCount(key); if(null != _factory) { _factory.destroyObject(key,obj); } notifyAll(); // _totalActive has changed } public synchronized void addObject(Object key) throws Exception { Object obj = _factory.makeObject(key); incrementActiveCount(key); // returnObject will decrement this returnObject(key,obj); } public int getNumIdle() { return _totIdle; } public int getNumActive() { return _totActive; } public synchronized int getNumActive(Object key) { return getActiveCount(key); } public synchronized int getNumIdle(Object key) { try { return((Stack)(_pools.get(key))).size(); } catch(Exception e) { return 0; } } public synchronized void clear() { Iterator it = _pools.keySet().iterator(); while(it.hasNext()) { Object key = it.next(); Stack stack = (Stack)(_pools.get(key)); destroyStack(key,stack); } _totIdle = 0; _pools.clear(); _activeCount.clear(); } public synchronized void clear(Object key) { Stack stack = (Stack)(_pools.remove(key)); destroyStack(key,stack); } private synchronized void destroyStack(Object 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(); } } 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()) { Object key = it.next(); buf.append(" |").append(key).append("|="); Stack s = (Stack)(_pools.get(key)); buf.append(s.size()); } return buf.toString(); } public synchronized void close() throws Exception { clear(); _pools = null; _factory = null; _activeCount = null; } public synchronized void setFactory(KeyedPoolableObjectFactory factory) throws IllegalStateException { if(0 < getNumActive()) { throw new IllegalStateException("Objects are already active"); } else { clear(); _factory = factory; } } private int getActiveCount(Object key) { try { return ((Integer)_activeCount.get(key)).intValue(); } catch(NoSuchElementException e) { return 0; } catch(NullPointerException e) { return 0; } } private void incrementActiveCount(Object key) { _totActive++; Integer old = (Integer)(_activeCount.get(key)); if(null == old) { _activeCount.put(key,new Integer(1)); } else { _activeCount.put(key,new Integer(old.intValue() + 1)); } } private void decrementActiveCount(Object key) { _totActive--; Integer active = (Integer)(_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)); } } /** 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. */ protected HashMap _pools = null; /** My {@link KeyedPoolableObjectFactory}. */ protected KeyedPoolableObjectFactory _factory = null; /** The cap on the number of "sleeping" instances in <i>each</i> pool. */ protected int _maxSleeping = DEFAULT_MAX_SLEEPING; /** The initial capacity of each pool. */ protected int _initSleepingCapacity = DEFAULT_INIT_SLEEPING_CAPACITY; /** Total number of object borrowed and not yet retuened for all pools */ protected int _totActive = 0; /** Total number of objects "sleeping" for all pools */ protected int _totIdle = 0; /** Number of active objects borrowed and not yet returned by pool */ protected HashMap _activeCount = null; }