--- /dev/null
+/*
+ * 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 pool107.org.apache.commons.pool.impl;
+
+import pool107.org.apache.commons.pool.BaseKeyedObjectPool;
+import pool107.org.apache.commons.pool.KeyedObjectPool;
+import pool107.org.apache.commons.pool.KeyedPoolableObjectFactory;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+
+/**
+ * A simple, <code>Stack</code>-based <code>KeyedObjectPool</code> 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 <code>Object</code>s without introducing
+ * artificial limits.
+ * </p>
+ *
+ * @author Rodney Waldhoff
+ * @author Sandy McArthur
+ * @version $Revision: 607154 $ $Date: 2007-12-27 20:37:53 -0500 (Thu, 27 Dec 2007) $
+ * @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((KeyedPoolableObjectFactory)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((KeyedPoolableObjectFactory)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((KeyedPoolableObjectFactory)null,max,init);
+ }
+
+ /**
+ * Create a new <code>SimpleKeyedObjectPool</code> using
+ * the specified <code>factory</code> 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 <code>SimpleKeyedObjectPool</code> using
+ * the specified <code>factory</code> to create new instances.
+ * capping the number of "sleeping" instances to <code>max</code>
+ *
+ * @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 <code>SimpleKeyedObjectPool</code> using
+ * the specified <code>factory</code> to create new instances.
+ * capping the number of "sleeping" instances to <code>max</code>,
+ * and initially allocating a container capable of containing
+ * at least <code>init</code> 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 {
+ assertOpen();
+ Stack stack = (Stack)(_pools.get(key));
+ if(null == stack) {
+ stack = new Stack();
+ stack.ensureCapacity( _initSleepingCapacity > _maxSleeping ? _maxSleeping : _initSleepingCapacity);
+ _pools.put(key,stack);
+ }
+ Object 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) {
+ try {
+ _factory.destroyObject(key,obj);
+ } catch (Throwable 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;
+ }
+
+ public synchronized void returnObject(Object key, Object 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 = (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 Object 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++;
+ }
+
+ public synchronized void invalidateObject(Object key, Object 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.
+ * <code>addObject</code> 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.
+ */
+ public synchronized void addObject(Object key) throws Exception {
+ assertOpen();
+ if (_factory == null) {
+ throw new IllegalStateException("Cannot add objects without a factory.");
+ }
+ Object 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 = (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 Object 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
+ */
+ 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
+ */
+ public int getNumActive() {
+ return _totActive;
+ }
+
+ /**
+ * Returns the number of instances currently borrowed from but not yet returned
+ * to the pool corresponding to the given <code>key</code>.
+ *
+ * @param key the key to query
+ * @return the number of instances corresponding to the given <code>key</code> currently borrowed in this pool
+ */
+ public synchronized int getNumActive(Object key) {
+ return getActiveCount(key);
+ }
+
+ /**
+ * Returns the number of instances corresponding to the given <code>key</code> currently idle in this pool.
+ *
+ * @param key the key to query
+ * @return the number of instances corresponding to the given <code>key</code> currently idle in this pool
+ */
+ public synchronized int getNumIdle(Object key) {
+ try {
+ return((Stack)(_pools.get(key))).size();
+ } catch(Exception e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Clears the pool, removing all pooled instances.
+ */
+ 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();
+ }
+
+ /**
+ * Clears the specified pool, removing all pooled instances corresponding to the given <code>key</code>.
+ *
+ * @param key the key to 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();
+ }
+
+ /**
+ * Close this pool, and free any resources associated with it.
+ * <p>
+ * Calling {@link #addObject addObject} or {@link #borrowObject borrowObject} after invoking
+ * this method on a pool will cause them to throw an {@link IllegalStateException}.
+ * </p>
+ *
+ * @throws Exception <strong>deprecated</strong>: implementations should silently fail if not all resources can be freed.
+ */
+ public void close() throws Exception {
+ super.close();
+ clear();
+ }
+
+ /**
+ * Sets the {@link KeyedPoolableObjectFactory factory} the pool uses
+ * to create new instances.
+ * Trying to change the <code>factory</code> after a pool has been used will frequently
+ * throw an {@link UnsupportedOperationException}.
+ *
+ * @param factory the {@link KeyedPoolableObjectFactory} used to create new instances.
+ * @throws IllegalStateException when the factory cannot be set at this time
+ */
+ 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 <code>each</code> 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;
+
+}