commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rdon...@apache.org
Subject svn commit: r387630 [2/4] - in /jakarta/commons/proper/pool/contrib/composite-pool: ./ java/ java/org/ java/org/mcarthur/ java/org/mcarthur/sandy/ java/org/mcarthur/sandy/commons/ java/org/mcarthur/sandy/commons/pool/ java/org/mcarthur/sandy/commons/po...
Date Tue, 21 Mar 2006 21:41:57 GMT
Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/CompositeObjectPoolFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/CompositeObjectPoolFactory.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/CompositeObjectPoolFactory.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/CompositeObjectPoolFactory.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,801 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.ObjectPoolFactory;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * {@link ObjectPoolFactory} that builds a custom {@link ObjectPool} via composition.
+ *
+ * <p>Default values for a newly created factory:
+ * <ul>
+ *  <li>{@link #setBorrowType(BorrowType) borrowType}:
+ *      {@link BorrowType#FIFO FIFO}</li>
+ *  <li>{@link #setExhaustionBehavior(ExhaustionBehavior) exhaustionBehavior}:
+ *      {@link ExhaustionBehavior#GROW GROW}</li>
+ *  <li>{@link #setMaxIdle(int) maxIdle}: a negative value (unlimited)</li>
+ *  <li>{@link #setMaxActive(int) maxActive}: a non-positve value (unlimited)</li>
+ *  <li>{@link #setLimitBehavior(LimitBehavior) limitBehavior}:
+ *      {@link LimitBehavior#FAIL FAIL}
+ *      (has no effect unless {@link #setMaxActive(int) maxActive} is positive)</li>
+ *  <li>{@link #setMaxWaitMillis(int) maxWaitMillis}: a non-positve value (wait forever)
+ *      (has no effect unless {@link #setLimitBehavior(LimitBehavior) limitBehavior} is
+ *      {@link LimitBehavior#WAIT WAIT})</li>
+ *  <li>{@link #setTrackerType(TrackingType) trackingType}:
+ *      {@link TrackingType#SIMPLE SIMPLE}</li>
+ *  <li>{@link #setValidateOnReturn(boolean) validateOnReturn}: false (do not validate on return)</li>
+ *  <li>{@link #setEvictIdleMillis(long) evictIdleMillis}: non-positive (do not evict objects for being idle)</li>
+ *  <li>{@link #setEvictInvalidFrequencyMillis(long) evictInvalidFrequencyMillis}: non-positive (do not check if idle
+ *      objects are {@link PoolableObjectFactory#validateObject(Object) invalid} and should be evicted)</li> 
+ * </ul>
+ * </p>
+ *
+ * <p>Example usages:</p>
+ *
+ * <p>To create a "stack" {@link ObjectPool} that keeps at most 5 idle objects, checks idle objects every 5 minutes to
+ * see if they are still {@link PoolableObjectFactory#validateObject(Object) valid} and evicts invalid idle objects,
+ * evicts idle objects after an hour regardless of wether or not they are
+ * {@link PoolableObjectFactory#validateObject(Object) valid}, and throws an {@link IllegalStateException} when you
+ * return an object that wasn't originally borrowed from the pool.
+ * <p>
+ * <pre>
+ * PoolableObjectFactory pof = ...;
+ * CompositeObjectPoolFactory copf = new CompositeObjectPoolFactory(pof);
+ * copf.setBorrowType(BorrowType.LIFO)
+ * copf.setMaxIdle(5);
+ * copf.setEvictInvalidFrequencyMillis(5 * 60 * 1000);
+ * copf.setEvictIdleMillis(60 * 60 * 1000);
+ * copf.setTrackingType(TrackingType.REFERENCE);
+ * ObjectPool pool = copf.createPool();
+ * </pre>
+ *
+ * <p>To create a fifo {@link ObjectPool} that does not automatically create new objects as needed, allows
+ * only 2 objects to be borrowed at a time, waits up to 10 seconds for an idle object to become available,
+ * and populates a created pool with 3 objects.
+ * <p>
+ * <pre>
+ * PoolableObjectFactory pof = ...;
+ * CompositeObjectPoolFactory copf = new CompositeObjectPoolFactory(pof);
+ * copf.setsetExhaustionBehavior(ExhaustionBehavior.FAIL);
+ * copf.setMaxActive(2);
+ * copf.setLimitBehavior(LimitBehavior.WAIT);
+ * copf.setMaxWaitMillis(10 * 1000);
+ * ObjectPool pool = copf.createPool();
+ * pool.addObject(); pool.addObject(); pool.addObject();
+ * </pre>
+ *
+ * <p>To create a fifo {@link ObjectPool} that doesn't prevent idle objects from being garabage collected, detects when
+ * borrowed objects are not returned to the pool and prints a stack trace from where they were borrowed.
+ * </p>
+ * <pre>
+ * PoolableObjectFactory pof = ...;
+ * CompositeObjectPoolFactory copf = new CompositeObjectPoolFactory(pof);
+ * copf.setBorrowType(BorrowType.SOFT_FIFO);
+ * copf.setTrackingType(TrackingType.DEBUG);
+ * ObjectPool pool = copf.createPool();
+ * </pre>
+ *
+ * <p>{@link ObjectPool}s created by this factory have the following properties:
+ * <ul>
+ *  <li>The must have a {@link ObjectPoolFactory}</li>
+ *  <li>They are thread-safe.</li>
+ *  <li>{@link ObjectPool#borrowObject() Borrowed objects} will either be
+ *      {@link PoolableObjectFactory#makeObject() newly created} or have been
+ *      {@link PoolableObjectFactory#activateObject(Object) activated} and
+ *      {@link PoolableObjectFactory#validateObject(Object) validated}.</li>
+ *  <li>Objects that fail {@link PoolableObjectFactory#validateObject(Object) validation} will be
+ *      {@link ObjectPool#invalidateObject(Object) invalidated}.</li>
+ *  <li>Objects that cause {@link PoolableObjectFactory object factories} to throw an exception during
+ *      {@link PoolableObjectFactory#activateObject(Object) activation} or
+ *      {@link PoolableObjectFactory#validateObject(Object) validation} will be
+ *      {@link PoolableObjectFactory#destroyObject(Object) destroyed} and removed from the pool. The pool will then try
+ *      again to return an object.</li>
+ *  <li>Exceptions thrown by {@link PoolableObjectFactory} methods except for {@link PoolableObjectFactory#makeObject()}
+ *      will be contained and dealt with.</li>
+ *  <li>The factory cannot be updated. {@link ObjectPool#setFactory(PoolableObjectFactory)} always throws an
+ *      {@link UnsupportedOperationException}.</li>
+ *  <li>Calling {@link ObjectPool#borrowObject()} or {@link ObjectPool#addObject()} after calling
+ *      {@link ObjectPool#close()} will throw an {@link IllegalStateException}.</li>
+ *  <li>The {@link ObjectPool#close()} method returned from this factory makes a good effort to free any resources
+ *      associated with the pool but it's possible for any threads currently executing in a pool method to leave behind
+ *      some non-freed resources. If you demand perfect behavior of the {@link ObjectPool#close()} method the wrap each
+ *      access to the pool in a synchronized block.</li>
+ *  <li>Deserialized {@link ObjectPool}s produced by this factory will not retain their idle objects. Active objects
+ *      borrowed from the serialized {@link ObjectPool} must not be returned to the deserialized {@link ObjectPool}.
+ *      All other behavior and settings of the {@link ObjectPool} will be maintained.</li>
+ *  <li>{@link ObjectPool}s created by this factory are {@link Cloneable}. Cloned instances do not retain the idle
+ *      objects of the original instance and any active objects borrowed from the originial must not be returned to the
+ *      new clone.</li>
+ * </ul>
+ * </p>
+ *
+ * @see BorrowType
+ * @see ExhaustionBehavior
+ * @see LimitBehavior
+ * @see TrackingType
+ * @author Sandy McArthur
+ * @version $Revision$ $Date$
+ * @since #.#
+ */
+public final class CompositeObjectPoolFactory implements ObjectPoolFactory, Cloneable, Serializable {
+
+    private static final long serialVersionUID = 2675590130354850408L;
+
+    /**
+     * Serialize access to {@link #config}.
+     * Once JDK 1.5 is required by pool this should be changed to a ReadWriteLock.
+     */
+    private final transient Object lock = new Object();
+
+    /**
+     * A cached struct of these values. Note: if any setter is called this must be set to null.
+     * @see #getConfig()
+     */
+    private transient FactoryConfig config = null;
+
+    /**
+     * The object factory to be used by pools created from this pool factory.
+     */
+    // XXX: Add better handling of when this instance is not Serializable
+    private PoolableObjectFactory factory;
+
+    /**
+     * Configured {@link Lender} type.
+     */
+    private BorrowType borrowType = BorrowType.FIFO;
+
+    /**
+     * Configured {@link Manager} type.
+     */
+    private ExhaustionBehavior exhaustionBehavior = ExhaustionBehavior.GROW;
+
+    /**
+     * Maximum number of idle objects in the pool.
+     * A negative value means unlimited.
+     * Zero means the pool will behave like a factory.
+     * A positve value limits the number of idle objects.
+     */
+    private int maxIdle = -1;
+
+    /**
+     * Maximum nuber of active objects from the pool. A non-positive value means unlimited.
+     *
+     * @see ActiveLimitManager
+     */
+    private int maxActive = -1;
+
+    /**
+     * Configured {@link ActiveLimitManager} type. Not used if {@link #maxActive} is non-positive.
+     */
+    private LimitBehavior limitBehavior = LimitBehavior.FAIL;
+
+    /**
+     * Configured max wait time for an available object. Non-positve means wait forever.
+     *
+     * @see WaitLimitManager
+     */
+    private int maxWaitMillis = -1;
+
+    /**
+     * Configued {@link Tracker} type.
+     */
+    private TrackingType trackerType = TrackingType.SIMPLE;
+
+    /**
+     * Should the object pool validate borrowed objects when they are reutrned.
+     */
+    private boolean validateOnReturn = false;
+
+    /**
+     * Idle timeout for idle objects to be evicted.
+     * A non-positive value means do not evict objects just because they are idle.
+     */
+    private long evictIdleMillis = -1;
+
+    /**
+     * Frequency idle objects should be checked to be still valid.
+     * A non-positive value means do not evict objects just because they fail to validate.
+     */
+    private long evictInvalidFrequencyMillis = -1;
+
+    /**
+     * Create a new object pool factory with the specified object factory.
+     *
+     * @param factory the object factory for this pool, must not be null.
+     */
+    public CompositeObjectPoolFactory(final PoolableObjectFactory factory) {
+        setFactory(factory);
+    }
+
+    /**
+     * Create and return a new {@link ObjectPool}.
+     *
+     * @return a new {@link ObjectPool}
+     */
+    public ObjectPool createPool() {
+        return createPool(getConfig());
+    }
+
+    /**
+     * Create and return a new {@link ObjectPool} based on the settings stored in <code>config</code>.
+     *
+     * @param config the settings to use to construct said pool.
+     * @return a new {@link ObjectPool}
+     */
+    static ObjectPool createPool(final FactoryConfig config) {
+        if (config == null) {
+            throw new IllegalArgumentException("config must not be null.");
+        }
+        return new CompositeObjectPool(config.factory, getList(config), getManager(config), getLender(config),
+                getTracker(config), config.validateOnReturn, config);
+    }
+
+    /**
+     * Choose a {@link List} implementation optimized for this pool's behavior.
+     *
+     * @param config
+     * @return a {@link List} implementation optimized for this pool's behavior.
+     */
+    private static List getList(final FactoryConfig config) {
+        final List list; // LIFO is more suited to an ArrayList, FIFO is more suited to a LinkedList
+        if (BorrowType.NULL.equals(config.borrowType) || config.maxIdle == 0) {
+            // an empty pool can use an empty list.
+            list = Collections.EMPTY_LIST;
+
+        } else if (BorrowType.LIFO.equals(config.borrowType) || BorrowType.SOFT_LIFO.equals(config.borrowType)) {
+            // pre-allocate the backing array if the max size is known.
+            if (config.maxIdle >= 0) {
+                list = new ArrayList(config.maxIdle);
+            } else {
+                list = new ArrayList();
+            }
+
+        } else {
+            // For small list sizes the cost of shuffling the items in an array down one spot
+            // is cheaper than for LinkList to manage it's internal stuctures.
+            // The threshold (10) was based on some benchmarks on some 1.4 and 1.5 JVMs was between 10 to 25
+            if (0 <= config.maxIdle && config.maxIdle <= 10) {
+                list = new ArrayList(config.maxIdle);
+            } else {
+                list = new LinkedList();
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Choose a {@link Lender} based on this factory's settings.
+     *
+     * @return a new lender for an object pool.
+     * @param config
+     */
+    private static Lender getLender(final FactoryConfig config) {
+        final BorrowType borrowType = config.borrowType;
+        Lender lender;
+        if (config.maxIdle != 0) {
+            if (BorrowType.FIFO.equals(borrowType)) {
+                lender = new FifoLender();
+            } else if (BorrowType.LIFO.equals(borrowType)) {
+                lender = new LifoLender();
+            } else if (BorrowType.SOFT_FIFO.equals(borrowType)) {
+                lender = new SoftLender(new FifoLender());
+            } else if (BorrowType.SOFT_LIFO.equals(borrowType)) {
+                lender = new SoftLender(new LifoLender());
+            } else if (BorrowType.NULL.equals(borrowType)) {
+                lender = new NullLender();
+            } else {
+                throw new IllegalStateException("No clue what this borrow type is: " + borrowType);
+            }
+        } else {
+            lender = new NullLender();
+        }
+
+        // If the lender is a NullLender then there is no point to evicting idle objects that aren't there.
+        if (!(lender instanceof NullLender)) {
+            // If the evictIdleMillis were less than evictInvalidFrequencyMillis
+            // then the InvalidEvictorLender would never run.
+            if (config.evictInvalidFrequencyMillis > 0 && config.evictIdleMillis > config.evictInvalidFrequencyMillis) {
+                lender = new InvalidEvictorLender(lender);
+                ((InvalidEvictorLender)lender).setValidationFrequencyMillis(config.evictInvalidFrequencyMillis);
+            }
+
+            if (config.evictIdleMillis > 0) {
+                lender = new IdleEvictorLender(lender);
+                ((IdleEvictorLender)lender).setIdleTimeoutMillis(config.evictIdleMillis);
+            }
+        }
+        return lender;
+    }
+
+    /**
+     * Compose a {@link Manager} based on this factory's settings.
+     *
+     * @param config
+     * @return a new manager for an object pool.
+     */
+    private static Manager getManager(final FactoryConfig config) {
+        Manager manager;
+        final ExhaustionBehavior exhaustionBehavior = config.exhaustionBehavior;
+        if (ExhaustionBehavior.GROW.equals(exhaustionBehavior)) {
+            manager = new GrowManager();
+        } else if (ExhaustionBehavior.FAIL.equals(exhaustionBehavior)) {
+            if (BorrowType.NULL.equals(config.borrowType)) {
+                throw new IllegalStateException("Using the NULL borrow type with the FAIL exhaustion behavior is pointless.");
+            } else if (config.maxIdle == 0) {
+                throw new IllegalStateException("Using the FAIL exhaustion behavior with a max of zero idle objects is pointless.");
+            }
+            manager = new FailManager();
+        } else {
+            throw new IllegalStateException("No clue what this exhaustion behavior is: " + exhaustionBehavior);
+        }
+
+        final int maxActive = config.maxActive;
+        if (maxActive > 0) {
+            if (TrackingType.NULL.equals(config.trackerType)) {
+                throw new IllegalStateException("Using the NULL tracker and limiting pool size is not valid.");
+            }
+            final LimitBehavior limitBehavior = config.limitBehavior;
+            if (LimitBehavior.FAIL.equals(limitBehavior)) {
+                manager = new FailLimitManager(manager);
+            } else if (LimitBehavior.WAIT.equals(limitBehavior)) {
+                manager = new WaitLimitManager(manager);
+                ((WaitLimitManager)manager).setMaxWaitMillis(config.maxWaitMillis);
+            } else {
+                throw new IllegalStateException("No clue what this wait behavior is: " + limitBehavior);
+            }
+            ((ActiveLimitManager)manager).setMaxActive(maxActive);
+        }
+
+        if (config.maxIdle > 0) {
+            manager = new IdleLimitManager(manager);
+            ((IdleLimitManager)manager).setMaxIdle(config.maxIdle);
+        }
+        return manager;
+    }
+
+    /**
+     * Choose a {@link Tracker} based on this factory's settings.
+     *
+     * @param config
+     * @return a new tracker for an object pool.
+     */
+    private static Tracker getTracker(final FactoryConfig config) {
+        final Tracker tracker;
+        final TrackingType trackerType = config.trackerType;
+        if (TrackingType.SIMPLE.equals(trackerType)) {
+            tracker = new SimpleTracker();
+        } else if (TrackingType.NULL.equals(trackerType)) {
+            tracker = new NullTracker();
+        } else if (TrackingType.REFERENCE.equals(trackerType)) {
+            tracker = new ReferenceTracker();
+        } else if (TrackingType.DEBUG.equals(trackerType)) {
+            tracker = new DebugTracker();
+        } else {
+            throw new IllegalStateException("No clue what this tracking type is: " + trackerType);
+        }
+        return tracker;
+    }
+
+    /**
+     * Create or use a cached {@link FactoryConfig} with the factory's current settings.
+     *
+     * @return this factory's current settings in a "struct".
+     */
+    private FactoryConfig getConfig() {
+        synchronized (lock) {
+            if (config == null) {
+                config = new FactoryConfig(this);
+            }
+            return config;
+        }
+    }
+
+    /**
+     * Object factory used by pools created from this factory.
+     *
+     * @return object factory used by pools created from this factory.
+     */
+    public PoolableObjectFactory getFactory() {
+        return factory;
+    }
+
+    /**
+     * Set the object factory used by pools created from this factory.
+     *
+     * @param factory the object factory to be used by pools.
+     * @throws IllegalArgumentException if <code>factory</code> is <code>null</code>.
+     */
+    public void setFactory(final PoolableObjectFactory factory) throws IllegalArgumentException {
+        if (factory == null) {
+            throw new IllegalArgumentException("object factory must not be null.");
+        }
+        synchronized (lock){
+            config = null;
+            this.factory = factory;
+        }
+    }
+
+    /**
+     * Order in which objects are borrowed from the pool.
+     *
+     * @return the order in which objects are pooled.
+     */
+    public BorrowType getBorrowType() {
+        return borrowType;
+    }
+
+    /**
+     * Set the order in which objects are borrowed from the pool.
+     *
+     * <p>Note: this doesn't mean much if {@link #setMaxIdle(int) maxIdle} is set to zero.</p>
+     *
+     * @param borrowType the order in which objects are pooled.
+     * @throws IllegalArgumentException when <code>borrowType</code> is <code>null</code>.
+     */
+    public void setBorrowType(final BorrowType borrowType) throws IllegalArgumentException {
+        if (borrowType == null) {
+            throw new IllegalArgumentException("borrow type must not be null.");
+        }
+        synchronized (lock){
+            config = null;
+            this.borrowType = borrowType;
+        }
+    }
+
+    /**
+     * Behavior of the pool when all idle objects have been exhasted.
+     *
+     * @return behavior of the pool when all idle objects have been exhasted.
+     */
+    public ExhaustionBehavior getExhaustionBehavior() {
+        return exhaustionBehavior;
+    }
+
+    /**
+     * Set the behavior for when the pool is exhausted of any idle objects.
+     *
+     * @param exhaustionBehavior the desired exhausted behavior of the pool.
+     * @throws IllegalArgumentException when <code>exhaustionBehavior</code> is <code>null</code>.
+     */
+    public void setExhaustionBehavior(final ExhaustionBehavior exhaustionBehavior) throws IllegalArgumentException {
+        if (exhaustionBehavior == null) {
+            throw new IllegalArgumentException("exhaustion behavior must not be null.");
+        }
+        synchronized (lock){
+            config = null;
+            this.exhaustionBehavior = exhaustionBehavior;
+        }
+    }
+
+    /**
+     * Maximum number of idle objects in the pool.
+     * A negative value means unlimited.
+     * Zero means the pool will behave like a factory.
+     * A positve value limits the number of idle objects.
+     *
+     * @return a non-negative value is the maximum number of idle objects in the pool, else unlimited.
+     */
+    public int getMaxIdle() {
+        return maxIdle;
+    }
+
+    /**
+     * Set the maximum number of idle objects in the pool.
+     * A negative value means unlimited.
+     * Zero means the pool will behave like a factory.
+     * A positve value limits the number of idle objects.
+     *
+     * @param maxIdle a non-negative value is the maximum number of idle objects in the pool, else unlimited.
+     */
+    public void setMaxIdle(final int maxIdle) {
+        synchronized (lock){
+            config = null;
+            this.maxIdle = maxIdle;
+        }
+    }
+
+    /**
+     * Maximum number of objects associated with this pool. A non-positive value means there is no limit.
+     *
+     * @return if &gt; 0 the the maximum number of objects else no size limit.
+     */
+    public int getMaxActive() {
+        return maxActive;
+    }
+
+    /**
+     * Set the maximum objects associated with this pool. Any non-positive value means there is no limit.
+     *
+     * @param maxActive the limit of active and idle objects in the pool or &lt;= 0 for no limit.
+     */
+    public void setMaxActive(final int maxActive) {
+        synchronized (lock){
+            config = null;
+            this.maxActive = maxActive;
+        }
+    }
+
+    /**
+     * Behavior of the pool when the limit of active objects has been reached.
+     *
+     * @return the behavior of the pool when the limit of active objects has been reached.
+     * @see #getMaxWaitMillis()
+     */
+    public LimitBehavior getLimitBehavior() {
+        return limitBehavior;
+    }
+
+    /**
+     * Set the behavior of when the pool's limit of active objects has been reached.
+     *
+     * @param limitBehavior action to take if the pool has an object limit and is exhausted.
+     * @throws IllegalArgumentException when <code>limitBehavior</code> is <code>null</code>.
+     * @see #setMaxWaitMillis(int)
+     */
+    public void setLimitBehavior(final LimitBehavior limitBehavior) throws IllegalArgumentException {
+        if (limitBehavior == null) {
+            throw new IllegalArgumentException("limit behavior must not be null.");
+        }
+        synchronized (lock){
+            config = null;
+            this.limitBehavior = limitBehavior;
+        }
+    }
+
+    /**
+     * Wait time in milli-seconds for an object to become available to the pool when the {@link LimitBehavior#WAIT WAIT}
+     * {@link #setLimitBehavior(LimitBehavior) limit behavior} is used.
+     *
+     * @return the wait time for an object to become available to the pool.
+     * @see #getLimitBehavior()
+     */
+    public int getMaxWaitMillis() {
+        return maxWaitMillis;
+    }
+
+    /**
+     * Set the wait time in milli-seconds for an object to become available to the pool when it was exhausted.
+     * This has no effect unless the {@link #setLimitBehavior(LimitBehavior) limit behavior}
+     * is set to {@link LimitBehavior#WAIT}.
+     *
+     * @param maxWaitMillis the milli-seconds to wait for an available object in the pool or &lt;= 0 for no limit.
+     * @see #setLimitBehavior(LimitBehavior)
+     */
+    public void setMaxWaitMillis(final int maxWaitMillis) {
+        synchronized (lock){
+            config = null;
+            this.maxWaitMillis = maxWaitMillis;
+        }
+    }
+
+    /**
+     * Type of tracking for active objects while they are borrowed from the pool.
+     *
+     * @return Type of tracking for active objects while they are borrowed from the pool.
+     */
+    public TrackingType getTrackerType() {
+        return trackerType;
+    }
+
+    /**
+     * Set the type of tracking for active objects while they are borrowed from the pool.
+     *
+     * @param trackerType type of tracking for active objects.
+     * @throws IllegalArgumentException when <code>trackerType</code> is <code>null</code>.
+     */
+    public void setTrackerType(final TrackingType trackerType) throws IllegalArgumentException {
+        if (trackerType == null) {
+            throw new IllegalArgumentException("tracker type must not be null.");
+        }
+        synchronized (lock){
+            config = null;
+            this.trackerType = trackerType;
+        }
+    }
+
+    /**
+     * Are objects validated when they are returned to the pool.
+     *
+     * @return are objects validated when they are returned to the pool.
+     */
+    public boolean isValidateOnReturn() {
+        return validateOnReturn;
+    }
+
+    /**
+     * Set if objects should be validated when returned to the pool.
+     *
+     * @param validateOnReturn <code>true</code> to validate objects when they are added to the pool.
+     */
+    public void setValidateOnReturn(final boolean validateOnReturn) {
+        synchronized (lock){
+            config = null;
+            this.validateOnReturn = validateOnReturn;
+        }
+    }
+
+    /**
+     * Idle timeout for idle objects to be evicted.
+     * A non-positive value means do not evict objects just because they are idle.
+     *
+     * @return if positive the time in milli-seconds to evict idle objects.
+     */
+    public long getEvictIdleMillis() {
+        synchronized (lock) {
+            return evictIdleMillis;
+        }
+    }
+
+    /**
+     * Set the idle timeout for idle objects to be evicted.
+     * A non-positive value means do not evict objects just because they are idle.
+     *
+     * @param evictIdleMillis if positive the time in milli-seconds to evict idle objects.
+     */
+    public void setEvictIdleMillis(final long evictIdleMillis) {
+        synchronized (lock){
+            config = null;
+            this.evictIdleMillis = evictIdleMillis;
+        }
+    }
+
+    /**
+     * Frequency idle objects should be checked to be still valid.
+     * A non-positive value means do not evict objects just because they fail to validate.
+     *
+     * @return if positive the frequency in milli-seconds to check that idle objects are still valid.
+     */
+    public long getEvictInvalidFrequencyMillis() {
+        synchronized (lock) {
+            return evictInvalidFrequencyMillis;
+        }
+    }
+
+    /**
+     * Set the frequency idle objects should be checked to be still valid.
+     * A non-positive value means do not evict objects just because they fail to validate.
+     *
+     * @param evictInvalidFrequencyMillis if positive the frequency in milli-seconds to check that
+     * idle objects are still valid.
+     */
+    public void setEvictInvalidFrequencyMillis(final long evictInvalidFrequencyMillis) {
+        synchronized (lock){
+            config = null;
+            this.evictInvalidFrequencyMillis = evictInvalidFrequencyMillis;
+        }
+    }
+
+    /**
+     * Create a copy of this factory with the existing values.
+     * @return a copy of this {@link ObjectPoolFactory}.
+     * @throws CloneNotSupportedException if {@link Object#clone()} does.
+     */
+    public Object clone() throws CloneNotSupportedException {
+        return super.clone();
+    }
+
+    public String toString() {
+        final StringBuffer sb = new StringBuffer();
+        sb.append("CompositeObjectPoolFactory{");
+        sb.append(getConfig());
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     * A "struct" to capture this factory's settings so {@link CompositeObjectPool}s can print friendly
+     * {@link Object#toString()} messages and be {@link Object#clone()}. For the meaning of each field see
+     * the field of {@link CompositeObjectPoolFactory} with the same name.
+     */
+    static final class FactoryConfig implements Serializable {
+        private static final long serialVersionUID = 8055395905602482612L;
+
+        /** @see CompositeObjectPoolFactory#factory */
+        // XXX: Add better handling of when this instance is not Serializable
+        private final PoolableObjectFactory factory;
+
+        /** @see CompositeObjectPoolFactory#borrowType */
+        private final BorrowType borrowType;
+
+        /** @see CompositeObjectPoolFactory#exhaustionBehavior */
+        private final ExhaustionBehavior exhaustionBehavior;
+
+        /** @see CompositeObjectPoolFactory#maxIdle */
+        private final int maxIdle;
+
+        /** @see CompositeObjectPoolFactory#maxActive */
+        private final int maxActive;
+
+        /** @see CompositeObjectPoolFactory#limitBehavior */
+        private final LimitBehavior limitBehavior;
+
+        /** @see CompositeObjectPoolFactory#maxWaitMillis */
+        private final int maxWaitMillis;
+
+        /** @see CompositeObjectPoolFactory#trackerType */
+        private final TrackingType trackerType;
+
+        /** @see CompositeObjectPoolFactory#validateOnReturn */
+        private final boolean validateOnReturn;
+
+        /** @see CompositeObjectPoolFactory#evictIdleMillis */
+        private final long evictIdleMillis;
+
+        /** @see CompositeObjectPoolFactory#evictInvalidFrequencyMillis */
+        private final long evictInvalidFrequencyMillis;
+
+        /**
+         * Convenience constuctor. This <b>must</b> be called from a synchronized context to be thread-safe.
+         */
+        FactoryConfig(final CompositeObjectPoolFactory copf) {
+            this(copf.getFactory(), copf.getBorrowType(), copf.getExhaustionBehavior(), copf.getMaxIdle(),
+                    copf.getMaxActive(), copf.getLimitBehavior(), copf.getMaxWaitMillis(), copf.getTrackerType(),
+                    copf.isValidateOnReturn(), copf.getEvictIdleMillis(), copf.getEvictInvalidFrequencyMillis());
+        }
+
+        FactoryConfig(final PoolableObjectFactory factory, final BorrowType borrowType,
+                      final ExhaustionBehavior exhaustionBehavior, final int maxIdle, final int maxActive,
+                      final LimitBehavior limitBehavior, final int maxWaitMillis, final TrackingType trackerType,
+                      final boolean validateOnReturn, final long evictIdleMillis,
+                      final long evictInvalidFrequencyMillis) {
+            this.factory = factory;
+            this.borrowType = borrowType;
+            this.exhaustionBehavior = exhaustionBehavior;
+            this.maxIdle = maxIdle;
+            this.maxActive = maxActive;
+            this.limitBehavior = limitBehavior;
+            this.maxWaitMillis = maxWaitMillis;
+            this.trackerType = trackerType;
+            this.validateOnReturn = validateOnReturn;
+            this.evictIdleMillis = evictIdleMillis;
+            this.evictInvalidFrequencyMillis = evictInvalidFrequencyMillis;
+        }
+
+
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("factory=").append(factory);
+            sb.append(", borrowType=").append(borrowType);
+            sb.append(", exhaustionBehavior=").append(exhaustionBehavior);
+            sb.append(", maxIdle=").append(maxIdle);
+            sb.append(", maxActive=").append(maxActive);
+            if (maxActive > 0) {
+                sb.append(", limitBehavior=").append(limitBehavior);
+                if (LimitBehavior.WAIT.equals(limitBehavior)) {
+                    sb.append(", maxWaitMillis=").append(maxWaitMillis);
+                }
+            }
+            sb.append(", trackerType=").append(trackerType);
+            sb.append(", validateOnReturn=").append(validateOnReturn);
+            if (evictIdleMillis > 0) {
+                sb.append(", evictIdleMillis=").append(evictIdleMillis);
+            }
+            if (evictInvalidFrequencyMillis > 0) {
+                sb.append(", evictInvalidFrequencyMillis=").append(evictInvalidFrequencyMillis);
+            }
+            return sb.toString();
+        }
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DebugTracker.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DebugTracker.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DebugTracker.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DebugTracker.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Keeps track of active objects with {@link Reference}s and logs when they are not returned to the pool.
+ *
+ * @see TrackingType#DEBUG
+ * @author Sandy McArthur
+ * @version $Revision$ $Date$
+ * @since #.#
+ */
+final class DebugTracker extends ReferenceTracker implements Serializable {
+
+    private static final long serialVersionUID = -5536120104213707789L;
+
+    /**
+     * Logger for lost objects.
+     */
+    private static final Logger LOGGER = Logger.getLogger(DebugTracker.class.getName());
+
+    /**
+     * Message for logger.
+     */
+    private static final String LOG_MESSAGE = "Borrowed object was not returned to the pool.";
+
+    /**
+     * Wrap the object in a reference that records the stack.
+     *
+     * @param obj the object to be wrapped.
+     * @return a {@link StackWeakReference} around obj.
+     */
+    protected IdentityReference wrapBorrowed(final Object obj) {
+        return new StackWeakReference(obj, rq);
+    }
+
+    /**
+     * Log that an object was lost.
+     *
+     * @param ref the reference to the lost former object.
+     */
+    protected void referenceToBeRemoved(final IdentityReference ref) {
+        LOGGER.log(Level.WARNING, LOG_MESSAGE, ((StackWeakReference)ref).getStack());
+    }
+
+    public String toString() {
+        return "DebugTracker{" +
+                "active=" + map.size() +
+                ", lost=" + getLost() +
+                "}";
+    }
+
+    /**
+     * A {@link WeakReference} that keeps track of the stack trace from when the reference was created.
+     */
+    private static final class StackWeakReference extends WeakReference implements IdentityReference {
+
+        /**
+         * The message for the {@link Throwable} that was used to capture a stack trace.
+         */
+        private static final String THROWABLE_MESSAGE = "Stack trace at time of borrow for: ";
+
+        /**
+         * The {@link System#identityHashCode(Object)} of the object.
+         */
+        private final int ident; // XXX: Going need something more than this on 64 bit systems.
+
+        /**
+         * The {@link Throwable} caputing the stack trace.
+         */
+        private final Throwable stack;
+
+        /**
+         * Calculate a key for <code>referent</code> and record the current stack.
+         * @param referent object the new weak reference will refer to
+         * @param q queue the weak reference is registered with
+         * @see WeakReference#WeakReference(Object, ReferenceQueue)
+         */
+        StackWeakReference(final Object referent, final ReferenceQueue q) {
+            super(referent, q);
+            ident = System.identityHashCode(referent);
+            // Once JSK 1.5 is a requirement of pool it may be worth using 1.5's Thread.getStackTrace()
+            stack = new Throwable(THROWABLE_MESSAGE + referent);
+        }
+
+        public IdentityKey getKey() {
+            return new IdentityKey(ident);
+        }
+
+        /**
+         * The {@link Throwable} that captured the stack trace.
+         *
+         * @return the captured the stack trace.
+         */
+        public Throwable getStack() {
+            return stack;
+        }
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateLender.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateLender.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateLender.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateLender.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import java.io.Serializable;
+import java.util.ListIterator;
+
+/**
+ * Delegates all work to another lender. Subclasses should call <code>super.method(...)</code> to access the delegates.
+ *
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+abstract class DelegateLender extends AbstractLender implements Serializable {
+
+    private static final long serialVersionUID = -4403177642421760774L;
+
+    /**
+     * The delegate lender. This is only accessed by subclasses by calling super.method(...).
+     */
+    private final Lender delegate;
+
+    /**
+     * Create a lender that delegates some behavior to another lender.
+     *
+     * @param delegate the lender to delegate to, must not be <code>null</code>.
+     * @throws IllegalArgumentException when <code>delegate</code> is <code>null</code>.
+     */
+    protected DelegateLender(final Lender delegate) throws IllegalArgumentException {
+        if (delegate == null) {
+            throw new IllegalArgumentException("delegate Lender must not be null.");
+        }
+        this.delegate = delegate;
+    }
+
+    /**
+     * Calls {@link AbstractLender#setCompositeObjectPool(CompositeObjectPool)} and the delegate's
+     * {@link Lender#setCompositeObjectPool(CompositeObjectPool)} methods.
+     *
+     * @param objectPool the pool to associate with.
+     */
+    public void setCompositeObjectPool(final CompositeObjectPool objectPool) throws IllegalArgumentException, IllegalStateException {
+        super.setCompositeObjectPool(objectPool);
+        delegate.setCompositeObjectPool(getObjectPool());
+    }
+
+    /**
+     * Calls the delegate's {@link Lender#borrow()} method.
+     *
+     * @return a previously idle object.
+     */
+    public Object borrow() {
+        return delegate.borrow();
+    }
+
+    /**
+     * Calls the delegate's {@link Lender#repay(Object)} method.
+     *
+     * @param obj the object to return to the idle object pool.
+     */
+    public void repay(final Object obj) {
+        delegate.repay(obj);
+    }
+
+    /**
+     * Calls the delegate's {@link Lender#listIterator()} method.
+     *
+     * @return a list iterator of the elements in this pool.
+     */
+    public ListIterator listIterator() {
+        return delegate.listIterator();
+    }
+
+    /**
+     * Calls the delegate's {@link Lender#size()} method.
+     *
+     * @return the size of the idle object pool the lender is accessing.
+     */
+    public int size() {
+        return delegate.size();
+    }
+
+    public String toString() {
+        return delegate.toString();
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateManager.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateManager.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateManager.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/DelegateManager.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+import java.util.NoSuchElementException;
+
+/**
+ * Delegates all work to another manager. Subclasses should call <code>super.method(...)</code> to access the delegates.
+ *
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+abstract class DelegateManager extends AbstractManager implements Serializable {
+
+    private static final long serialVersionUID = -8516695099130531284L;
+
+    /**
+     * The manager that actually handles the interaction with the pool.
+     * This is only accessed by subclasses by calling super.method(...).
+     */
+    private final Manager delegate;
+
+    /**
+     * Create a manager that delegates some behavior to another manager.
+     *
+     * @param delegate the manager to delegate to, must not be <code>null</code>.
+     * @throws IllegalArgumentException when <code>delegate</code> is <code>null</code>.
+     */
+    protected DelegateManager(final Manager delegate) throws IllegalArgumentException {
+        if (delegate == null) {
+            throw new IllegalArgumentException("delegate must not be null.");
+        }
+        this.delegate = delegate;
+    }
+
+    /**
+     * Called once to associate this limit manager with an object pool by the {@link CompositeObjectPool} constructor.
+     * This also sets the {@link Manager#setCompositeObjectPool(CompositeObjectPool)} for the {@link #delegate}.
+     *
+     * @param objectPool the pool to associate with.
+     * @throws IllegalArgumentException if <code>objectPool</code> is <code>null</code>.
+     * @throws IllegalStateException if this method is called more than once.
+     */
+    public void setCompositeObjectPool(final CompositeObjectPool objectPool) {
+        super.setCompositeObjectPool(objectPool);
+        delegate.setCompositeObjectPool(objectPool);
+    }
+
+    /**
+     * Delegates to another {@link Manager}.
+     *
+     * @return a new or activated object.
+     * @throws NoSuchElementException if the pool is empty and no new object can be created.
+     * @throws Exception              usually from {@link PoolableObjectFactory} methods.
+     */
+    public Object nextFromPool() throws Exception {
+        return delegate.nextFromPool();
+    }
+
+    /**
+     * Delegates to another {@link Manager}.
+     *
+     * @param obj the object to return to the pool.
+     * @throws Exception as thrown by {@link PoolableObjectFactory#passivateObject(Object)}.
+     */
+    public void returnToPool(final Object obj) throws Exception {
+        delegate.returnToPool(obj);
+    }
+
+    public String toString() {
+        return delegate.toString();
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/EvictorLender.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/EvictorLender.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/EvictorLender.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/EvictorLender.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Base class for a {@link Lender} that evicts objects from the idle object pool.
+ *
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+abstract class EvictorLender extends DelegateLender implements Serializable {
+
+    private static final long serialVersionUID = 4040627184050939757L;
+
+    /**
+     * Shared evictor timer used by all {@link EvictorLender}s.
+     */
+    private static final Timer EVICTOR = new Timer(true);
+
+    /**
+     * If this evictor delegates to another evictor then don't bother pruning when {@link #size()} is called because the
+     * delegate will do that too.
+     */
+    private final boolean prune;
+
+    /**
+     * Create a lender that may evict objects from the idle object pool.
+     *
+     * @param delegate delegate the lender to delegate to, must not be <code>null</code>.
+     * @throws IllegalArgumentException when <code>delegate</code> is <code>null</code>.
+     */
+    protected EvictorLender(final Lender delegate) throws IllegalArgumentException {
+        super(delegate);
+        prune = !(delegate instanceof EvictorLender);
+    }
+
+    /**
+     * Calls the delegate's {@link Lender#borrow()} method and unwrapps any {@link EvictorLender.EvictorReference}.
+     *
+     * @return a previously idle object.
+     */
+    public Object borrow() {
+        final EvictorReference ref = (EvictorReference)super.borrow();
+        Object obj = null;
+        if (ref != null) {
+            obj = ref.get();
+            ref.clear();
+        }
+        return obj;
+    }
+
+    /**
+     * Calls the delegate's {@link Lender#repay(Object)} method.
+     * Calls {@link #createReference(Object)} to wrap the object.
+     *
+     * @param obj the object to return to the idle object pool.
+     */
+    public void repay(final Object obj) {
+        super.repay(createReference(obj));
+    }
+
+    public ListIterator listIterator() {
+        return new EvictorListIterator(super.listIterator());
+    }
+
+    /**
+     * Return the size of the idle object pool. Also removes any broken {@link EvictorLender.EvictorReference}s so the
+     * size is more accurate.
+     *
+     * @return the size of the idle object pool the lender is accessing.
+     */
+    public int size() {
+        if (prune) {
+            synchronized (getObjectPool().getPool()) {
+                final Iterator iter = super.listIterator();
+                while (iter.hasNext()) {
+                    final EvictorReference ref = (EvictorReference)iter.next();
+                    if (ref.get() == null) {
+                        iter.remove();
+                    }
+                }
+            }
+        }
+        return super.size();
+    }
+
+    /**
+     * Wrap an idle object in an {@link EvictorLender.EvictorReference} and schedule it's eviction with the
+     * {@link #getTimer() evictor timer}. {@link TimerTask}s used by the evictor timer must synchronize on the
+     * {@link CompositeObjectPool#getPool() idle object pool} to be thread-safe.
+     *
+     * @param obj idle object to be wrapped in an {@link EvictorLender.EvictorReference}.
+     * @return the wrapped idle object.
+     */
+    protected abstract EvictorReference createReference(Object obj);
+
+    /**
+     * Get the {@link Timer} used for eviction by this {@link EvictorLender}.
+     *
+     * @return the {@link Timer} used for evictions.
+     */
+    protected final Timer getTimer() {
+        // Future version may want to manage more than one TimerTask. For now one is fine.
+        return EVICTOR;
+    }
+
+    /**
+     * This is designed to mimick the {@link Reference} api.
+     * The only reason a {@link Reference} subclass isn't used is there is no "StrongReference" implementation.
+     */
+    protected interface EvictorReference {
+        /**
+         * Returns this evictor reference's referent.
+         *
+         * @return The object to which this evictor reference refers,
+         * or <code>null<code> if this reference object has been cleared.
+         * @see Reference#get()
+         */
+        public Object get();
+
+        /**
+         * Clears this reference.
+         *
+         * @see Reference#clear()
+         */
+        public void clear();
+    }
+
+    /**
+     * A {@link ListIterator} that unwrapps {@link EvictorLender.EvictorReference}s.
+     */
+    private static class EvictorListIterator implements ListIterator {
+        private final ListIterator iter;
+
+        private EvictorListIterator(final ListIterator iter) {
+            this.iter = iter;
+        }
+
+        public boolean hasNext() {
+            return iter.hasNext();
+        }
+
+        /**
+         * Unwrap an {@link EvictorLender.EvictorReference} and return the next object if it hasn't been evicted.
+         *
+         * @return an unwrapped object or <code>null</code> if an object has been evicted.
+         * @see ListIterator#next()
+         */
+        public Object next() {
+            final EvictorReference ref = (EvictorReference)iter.next();
+            return ref != null ? ref.get() : null;
+        }
+
+        public boolean hasPrevious() {
+            return iter.hasPrevious();
+        }
+
+        /**
+         * Unwrap an {@link EvictorLender.EvictorReference} and return the previous object if it hasn't been evicted.
+         *
+         * @return an unwrapped object or <code>null</code> if an object has been evicted.
+         * @see ListIterator#previous()
+         */
+        public Object previous() {
+            final EvictorReference ref = (EvictorReference)iter.previous();
+            return ref != null ? ref.get() : null;
+        }
+
+        public int nextIndex() {
+            return iter.nextIndex();
+        }
+
+        public int previousIndex() {
+            return iter.previousIndex();
+        }
+
+        public void remove() {
+            iter.remove();
+        }
+
+        public void set(final Object o) {
+            iter.set(o);
+        }
+
+        public void add(final Object o) {
+            iter.add(o);
+        }
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/ExhaustionBehavior.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/ExhaustionBehavior.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/ExhaustionBehavior.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/ExhaustionBehavior.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.ObjectPool;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.NoSuchElementException;
+
+/**
+ * Specifies the behavor of the pool when the pool is out of idle objects.
+ *
+ * @see CompositeObjectPoolFactory#setExhaustionBehavior(ExhaustionBehavior)
+ * @see CompositeKeyedObjectPoolFactory#setExhaustionBehavior(ExhaustionBehavior)
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+public final class ExhaustionBehavior implements Serializable {
+
+    private static final long serialVersionUID = -4895490364329810018L;
+
+    /**
+     * Grow the pool when all idle objects have been exhausted.
+     */
+    public static final ExhaustionBehavior GROW = new ExhaustionBehavior("GROW");
+
+    /**
+     * Throw a {@link NoSuchElementException} when all idle objects have been exhaused. Clients of the poll must
+     * call {@link ObjectPool#addObject()} to prefill the pool.
+     */
+    public static final ExhaustionBehavior FAIL = new ExhaustionBehavior("FAIL");
+
+    /**
+     * enum name.
+     */
+    private final String name;
+
+    private ExhaustionBehavior(final String name) {
+        this.name = name;
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    // Autogenerated with Java 1.5 enums
+    public static ExhaustionBehavior[] values() {
+        return new ExhaustionBehavior[] {FAIL, GROW};
+    }
+
+    // necessary for serialization
+    private static int nextOrdinal = 0;
+    private final int ordinal = nextOrdinal++;
+    private Object readResolve() throws ObjectStreamException {
+        return values()[ordinal];
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailLimitManager.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailLimitManager.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailLimitManager.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailLimitManager.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+import java.util.NoSuchElementException;
+
+/**
+ * Throws {@link NoSuchElementException} when the max number of active objects has been reached.
+ *
+ * @see LimitBehavior#FAIL
+ * @author Sandy McArthur
+ * @version $Revision$ $Date$
+ * @since #.#
+ */
+final class FailLimitManager extends ActiveLimitManager implements Serializable {
+
+    private static final long serialVersionUID = 4055528475643314990L;
+
+    /**
+     * Create a manager that when the limit of active objects has been reached throws a {@link NoSuchElementException}.
+     *
+     * @param delegate the manager to delegate to, must not be <code>null</code>.
+     * @throws IllegalArgumentException when <code>delegate</code> is <code>null</code>.
+     */
+    FailLimitManager(final Manager delegate) throws IllegalArgumentException {
+        super(delegate);
+    }
+
+    /**
+     * Checks to see how many total objects are in play and will not allow more than that.
+     * Delegates object management to another {@link Manager}.
+     *
+     * @return a new or activated object.
+     * @throws NoSuchElementException if the pool is empty and no new object can be created.
+     * @throws Exception usually from {@link PoolableObjectFactory} methods.
+     */
+    public Object nextFromPool() throws NoSuchElementException, Exception {
+        if (objectPool.getNumActive() < getMaxActive()) {
+            return super.nextFromPool();
+        } else {
+            throw new NoSuchElementException("No more than " + getMaxActive() + " objects allowed from this pool. Currently " + objectPool.getNumActive() + " have been borrowed.");
+        }
+    }
+
+    public String toString() {
+        return "FailLimitManager{" +
+                "maxActive=" + getMaxActive() +
+                ", delegate=" + super.toString() +
+                "}";
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailManager.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailManager.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailManager.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FailManager.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+import java.util.NoSuchElementException;
+
+/**
+ * Throws a {@link NoSuchElementException} when the idle pool is exhausted. If you want to add objects to the pool you
+ * should call {@link ObjectPool#addObject()}.
+ *
+ * @see ExhaustionBehavior#FAIL
+ * @author Sandy McArthur
+ * @version $Revision$ $Date$
+ * @since #.#
+ */
+final class FailManager extends AbstractManager implements Serializable {
+
+    private static final long serialVersionUID = 2245468648709521897L;
+
+    /**
+     * Thread local to hold on to the first cause that prevented us from providing an object from the pool.
+     * This must be cleared else it will either leak memory or provide confusing results in future invocations of
+     * {@link #nextFromPool()}.
+     */
+    private final transient ThreadLocal cause = new ThreadLocal();
+
+    /**
+     * Retreives the next object from the pool. Objects from the pool will be
+     * {@link PoolableObjectFactory#activateObject(Object) activated} and
+     * {@link PoolableObjectFactory#validateObject(Object) validated}.
+     * No objects will be {@link PoolableObjectFactory#makeObject() created}. Use {@link ObjectPool#addObject()} to
+     * populate the pool.
+     *
+     * @return a new or activated object.
+     * @throws NoSuchElementException if the pool is empty and no new object can be created.
+     * @throws Exception usually from {@link PoolableObjectFactory} methods.
+     */
+    public Object nextFromPool() throws Exception {
+        assert Thread.holdsLock(objectPool.getPool());
+        Object obj = null;
+        // Drain until good or empty
+        while (objectPool.getLender().size() > 0 && obj == null) {
+            obj = objectPool.getLender().borrow();
+
+            if (obj != null) {
+                obj = activateOrDestroy(obj);
+
+                try {
+                    if (obj != null && !objectPool.getFactory().validateObject(obj)) {
+                        objectPool.invalidateObject(obj);
+                        obj = null; // try again
+                    }
+                } catch (Exception e1) {
+                    updateCause(e1);
+                    try {
+                        objectPool.getFactory().destroyObject(obj);
+                    } catch (Exception e2) {
+                        // ignore
+                    }
+                    obj = null; // try again
+                }
+            }
+        }
+
+        if (obj == null) {
+            final Throwable t = (Throwable)cause.get();
+            final NoSuchElementException nsee = new NoSuchElementException("Pool configued to fail when exhausted.");
+            if (t != null) {
+                nsee.initCause(t);
+                cause.set(null); // clear reference
+            }
+            throw nsee;
+        }
+
+        cause.set(null); // clear reference
+        return obj;
+    }
+
+    /**
+     * {@link PoolableObjectFactory#activateObject(Object) Activate} an object or if that fails
+     * {@link PoolableObjectFactory#destroyObject(Object) destroy} it. If an exception is thrown it is saved to the
+     * {@link #cause} so that it can be part of the {@link NoSuchElementException} if this manager fails to get another
+     * object.
+     *
+     * @param obj the object to be activated or destroyed.
+     * @return the activated object or null if it was destroyed.
+     */
+    private Object activateOrDestroy(final Object obj) {
+        try {
+            objectPool.getFactory().activateObject(obj);
+        } catch (Exception e1) {
+            updateCause(e1);
+            try {
+                objectPool.getFactory().destroyObject(obj);
+            } catch (Exception e2) {
+                updateCause(e2);
+            }
+            return null; // try again
+        }
+        return obj;
+    }
+
+    /**
+     * Set the {@link #cause} for failure if it hasn't been set yet.
+     *
+     * @param t the real {@link Throwable#initCause(Throwable) cause}.
+     */
+    private void updateCause(final Throwable t) {
+        final Throwable previousCause = (Throwable)cause.get();
+        if (previousCause == null) {
+            cause.set(t);
+        }
+    }
+
+    public String toString() {
+        return "FailManager{}";
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FifoLender.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FifoLender.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FifoLender.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/FifoLender.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A First In First Out (FIFO) {@link Lender}.
+ *
+ * @see BorrowType#FIFO
+ * @author Sandy McArthur
+ * @version $Revision$ $Date$
+ * @since #.#
+ */
+final class FifoLender extends AbstractLender implements Serializable {
+
+    private static final long serialVersionUID = 8586173154375349810L;
+
+    public Object borrow() {
+        final List pool = getObjectPool().getPool();
+        assert Thread.holdsLock(pool);
+        if (pool instanceof LinkedList) {
+            return ((LinkedList)pool).removeFirst();
+        } else {
+            return pool.remove(0);
+        }
+    }
+
+    public String toString() {
+        return "FIFO";
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/GrowManager.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/GrowManager.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/GrowManager.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/GrowManager.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+
+/**
+ * Grows the pool automatically when it is exhausted.
+ * Whe the idle object pool is exhausted a new new object will be created via
+ * {@link PoolableObjectFactory#makeObject()}.
+ *
+ * @see ExhaustionBehavior#GROW
+ * @author Sandy McArthur
+ * @version $Revision$ $Date$
+ * @since #.#
+ */
+final class GrowManager extends AbstractManager implements Serializable {
+
+    private static final long serialVersionUID = 1225746308358794900L;
+
+    /**
+     * Retreives the next object from the pool, creating new objects if the pool has been exhausted.
+     *
+     * @return a new or activated object.
+     * @throws Exception when {@link PoolableObjectFactory#makeObject()} fails.
+     */
+    public Object nextFromPool() throws Exception {
+        assert Thread.holdsLock(objectPool.getPool());
+        Object obj = null;
+        // Drain until good or empty
+        while (objectPool.getLender().size() > 0 && obj == null) {
+            obj = objectPool.getLender().borrow();
+
+            if (obj != null) {
+                obj = activateOrDestroy(obj);
+
+                try {
+                    if (obj != null && !objectPool.getFactory().validateObject(obj)) {
+                        objectPool.invalidateObject(obj);
+                        obj = null; // try again
+                    }
+                } catch (Exception e1) {
+                    try {
+                        objectPool.getFactory().destroyObject(obj);
+                    } catch (Exception e2) {
+                        // ignore
+                    }
+                    obj = null; // try again
+                }
+            }
+        }
+
+        if (obj == null) {
+            obj = objectPool.getFactory().makeObject();
+        }
+        return obj;
+    }
+
+    /**
+     * {@link PoolableObjectFactory#activateObject(Object) Activate} an object or if that fails
+     * {@link PoolableObjectFactory#destroyObject(Object) destroy} it.
+     *
+     * @param obj the object to be activated or destroyed.
+     * @return the activated object or null if it was destroyed.
+     */
+    private Object activateOrDestroy(final Object obj) {
+        try {
+            objectPool.getFactory().activateObject(obj);
+        } catch (Exception e1) {
+            try {
+                objectPool.getFactory().destroyObject(obj);
+            } catch (Exception e2) {
+                // ignore
+            }
+            return null; // try again
+        }
+        return obj;
+    }
+
+    public String toString() {
+        return "GrowManager{}";
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleEvictorLender.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleEvictorLender.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleEvictorLender.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleEvictorLender.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import java.io.Serializable;
+import java.util.TimerTask;
+
+/**
+ * A {@link Lender} that evicts objects that have been idle for a while.
+ *
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+final class IdleEvictorLender extends EvictorLender implements Serializable {
+
+    private static final long serialVersionUID = 2422278988668384937L;
+
+    /**
+     * Time, in milli-seconds, before the idle objects are evicted.
+     */
+    private long idleTimeoutMillis = 60L * 60L * 1000L; // 60 minute
+
+    /**
+     * Create a lender that will evict idle objects after a period of time.
+     *
+     * @param delegate delegate the lender to delegate to, must not be <code>null</code>.
+     * @throws IllegalArgumentException when <code>delegate</code> is <code>null</code>.
+     */
+    IdleEvictorLender(final Lender delegate) throws IllegalArgumentException {
+        super(delegate);
+    }
+
+    protected EvictorReference createReference(final Object obj) {
+        return new IdleEvictorReference(obj);
+    }
+
+    /**
+     * Get the time, in milli-seconds, before the idle objects are evicted.
+     * @return the time, in milli-seconds, before the idle objects are evicted.
+     */
+    public long getIdleTimeoutMillis() {
+        return idleTimeoutMillis;
+    }
+
+    /**
+     * Set the time, in milli-seconds, before the idle objects are evicted.
+     *
+     * @param idleTimeoutMillis the time, in milli-seconds, before the idle objects are evicted.
+     */
+    public void setIdleTimeoutMillis(final long idleTimeoutMillis) {
+        this.idleTimeoutMillis = idleTimeoutMillis;
+    }
+
+    public String toString() {
+        return "IdleEvictor{" +
+                "idleTimeoutMillis=" + idleTimeoutMillis +
+                ", delegate=" + super.toString() +
+                '}';
+    }
+
+    /**
+     * An evictor reference that allows idle objects to be evicted for being idle for a period of time.
+     */
+    private class IdleEvictorReference implements EvictorReference {
+
+        /**
+         * The idle object.
+         */
+        private Object referant;
+
+        /**
+         * The timer task that when run will evict the idle object.
+         */
+        private final TimerTask task;
+
+        /**
+         * Create an evictor reference that allows idle objects to be evicted for being idle for a period of time.
+         *
+         * @param referant the idle object.
+         */
+        IdleEvictorReference(final Object referant) {
+            this.referant = referant;
+            task = new IdleEvictorTask();
+            getTimer().schedule(task, idleTimeoutMillis);
+        }
+
+        public Object get() {
+            return referant;
+        }
+
+        public void clear() {
+            task.cancel();
+            if (referant instanceof EvictorReference) {
+                ((EvictorReference)referant).clear();
+            }
+            referant = null;
+        }
+
+        /**
+         * A timer task that when run evicts the idle object.
+         */
+        private class IdleEvictorTask extends TimerTask {
+
+            /**
+             * Evict the idle object.
+             */
+            public void run() {
+                synchronized(getObjectPool().getPool()) {
+                    referant = null;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleLimitManager.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleLimitManager.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleLimitManager.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/IdleLimitManager.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+import java.util.ListIterator;
+
+/**
+ * A {@link Manager} that limit the number of idle objects associate with the pool.
+ *
+ * @see CompositeObjectPoolFactory#setMaxIdle(int)
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+final class IdleLimitManager extends DelegateManager implements Serializable {
+
+    private static final long serialVersionUID = -8037318859951361774L;
+
+    /**
+     * Maximum number of idle objects in the idle object pool.
+     */
+    private int maxIdle = 0;
+
+    IdleLimitManager(final Manager delegate) throws IllegalArgumentException {
+        super(delegate);
+    }
+
+    /**
+     * Possible remove an idle object and delegates to another {@link Manager}.
+     *
+     * @param obj the object to return to the pool.
+     * @throws Exception as thrown by {@link PoolableObjectFactory#passivateObject(Object)}.
+     */
+    public void returnToPool(final Object obj) throws Exception {
+        assert Thread.holdsLock(objectPool.getPool());
+        if (maxIdle > 0 && maxIdle <= objectPool.getNumIdle()) {
+            // XXX Does this remove the most stale object in
+            final ListIterator iter = objectPool.getLender().listIterator();
+            iter.next();
+            iter.remove();
+        }
+        super.returnToPool(obj);
+    }
+
+    /**
+     * Maximum number of idle objects in the idle object pool.
+     *
+     * @return maximum number of idle objects in the idle object pool.
+     */
+    public int getMaxIdle() {
+        return maxIdle;
+    }
+
+    /**
+     * Set the maximum number of idle objects in the idle object pool.
+     *
+     * @param maxIdle maximum number of idle objects.
+     */
+    public void setMaxIdle(final int maxIdle) {
+        this.maxIdle = maxIdle;
+    }
+
+    public String toString() {
+        return "IdleLimitManager{" +
+                "maxIdle=" + maxIdle +
+                ", delegate=" + super.toString() +
+                '}';
+    }
+}
\ No newline at end of file

Added: jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/InvalidEvictorLender.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/InvalidEvictorLender.java?rev=387630&view=auto
==============================================================================
--- jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/InvalidEvictorLender.java (added)
+++ jakarta/commons/proper/pool/contrib/composite-pool/java/org/mcarthur/sandy/commons/pool/composite/InvalidEvictorLender.java Tue Mar 21 13:41:53 2006
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2006 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 org.mcarthur.sandy.commons.pool.composite;
+
+import org.apache.commons.pool.PoolableObjectFactory;
+
+import java.io.Serializable;
+import java.util.TimerTask;
+
+/**
+ * A {@link Lender} that evicts objects from the idle pool if they fail
+ * {@link PoolableObjectFactory#validateObject(Object) validation}. 
+ *
+ * @author Sandy McArthur
+ * @since #.#
+ * @version $Revision$ $Date$
+ */
+final class InvalidEvictorLender extends EvictorLender implements Serializable {
+
+    private static final long serialVersionUID = -3200445766813431919L;
+
+    /**
+     * Time, in milli-seconds, between the checks that idle objects are still
+     * {@link PoolableObjectFactory#validateObject(Object) valid}.
+     */
+    private long validationFrequencyMillis = 10L * 60L * 1000L; // 10 minute
+
+    /**
+     * Create a lender that will evict idle object if they fail to pass
+     * {@link PoolableObjectFactory#validateObject(Object)}.
+     *
+     * @param delegate delegate the lender to delegate to, must not be <code>null</code>.
+     * @throws IllegalArgumentException when <code>delegate</code> is <code>null</code>.
+     */
+    InvalidEvictorLender(final Lender delegate) throws IllegalArgumentException {
+        super(delegate);
+    }
+
+    protected EvictorReference createReference(final Object obj) {
+        return new InvalidEvictorReference(obj);
+    }
+
+    /**
+     * Get the time, in milli-seconds, between the checks that idle objects are still
+     * {@link PoolableObjectFactory#validateObject(Object) valid}.
+     *
+     * @return time, in milli-seconds, between the checks that idle objects are still valid.
+     */
+    public long getValidationFrequencyMillis() {
+        return validationFrequencyMillis;
+    }
+
+    /**
+     * Set the time, in milli-seconds, between the checks that idle objects are still
+     * {@link PoolableObjectFactory#validateObject(Object) valid}.
+     *
+     * @param validationFrequencyMillis time, in milli-seconds, between the checks that idle objects are still valid.
+     * @throws IllegalArgumentException if validationFrequencyMillis is negative
+     */
+    public void setValidationFrequencyMillis(final long validationFrequencyMillis) throws IllegalArgumentException {
+        if (validationFrequencyMillis < 0) {
+            throw new IllegalArgumentException("validationFrequencyMillis must not be negative. was: " + validationFrequencyMillis);
+        }
+        this.validationFrequencyMillis = validationFrequencyMillis;
+    }
+
+    public String toString() {
+        return "InvalidEvictor{" +
+                "validationFrequencyMillis=" + validationFrequencyMillis +
+                ", delegate=" + super.toString() +
+                '}';
+    }
+
+    /**
+     * An evictor reference that evicts idle objects that fail
+     * {@link PoolableObjectFactory#validateObject(Object) validation}.
+     */
+    private class InvalidEvictorReference implements EvictorReference {
+
+        /**
+         * The idle object.
+         */
+        private Object referant;
+
+        /**
+         * The timer task that when run will validate and possibly evict the idle object.
+         */
+        private final TimerTask task;
+
+        /**
+         * Create an evictor reference that checks if idle objects are still valid.
+         *
+         * @param referant the idle object.
+         */
+        InvalidEvictorReference(final Object referant) {
+            this.referant = referant;
+            task = new InvalidEvictorTask();
+            getTimer().schedule(task, validationFrequencyMillis, validationFrequencyMillis);
+        }
+
+        public Object get() {
+            return referant;
+        }
+
+        public void clear() {
+            task.cancel();
+            if (referant instanceof EvictorReference) {
+                ((EvictorReference)referant).clear();
+            }
+            referant = null;
+        }
+
+        /**
+         * A TimerTask that checks the {@link PoolableObjectFactory#validateObject(Object) validity} of it idle object.
+         * If the idle object fails validation then the {@link InvalidEvictorLender.InvalidEvictorReference} is
+         * {@link InvalidEvictorReference#clear() cleared}.
+         */
+        private class InvalidEvictorTask extends TimerTask {
+
+            /**
+             * Check the idle object for {@link PoolableObjectFactory#validateObject(Object) validity} and
+             * {@link InvalidEvictorReference#clear() clear} it if it fails.
+             */
+            public void run() {
+                // Skip some synchronization if we can
+                if (referant == null) {
+                    cancel();
+                    return;
+                }
+
+                final PoolableObjectFactory factory = getObjectPool().getFactory();
+                synchronized(getObjectPool().getPool()) {
+                    if (referant == null) {
+                        cancel();
+                        return;
+                    }
+                    try {
+                        factory.activateObject(referant);
+                        if (factory.validateObject(referant)) {
+                            factory.passivateObject(referant);
+                        } else {
+                            factory.destroyObject(referant);
+                            clear();
+                        }
+                    } catch (Exception e) {
+                        clear();
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message