You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/03/13 16:30:51 UTC

[commons-pool] branch master updated: Fix Javadoc nits.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git


The following commit(s) were added to refs/heads/master by this push:
     new be57d5a  Fix Javadoc nits.
be57d5a is described below

commit be57d5ac62318fc9db1372a206b1b0c802d48616
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Mar 13 12:30:47 2019 -0400

    Fix Javadoc nits.
---
 .../commons/pool2/impl/GenericObjectPool.java      | 2411 ++++++++++----------
 1 file changed, 1214 insertions(+), 1197 deletions(-)

diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
index 7aff5f4..9158d3b 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -1,1197 +1,1214 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.pool2.impl;
-
-import org.apache.commons.pool2.ObjectPool;
-import org.apache.commons.pool2.PoolUtils;
-import org.apache.commons.pool2.PooledObject;
-import org.apache.commons.pool2.PooledObjectFactory;
-import org.apache.commons.pool2.PooledObjectState;
-import org.apache.commons.pool2.SwallowedExceptionListener;
-import org.apache.commons.pool2.TrackedUse;
-import org.apache.commons.pool2.UsageTracking;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * A configurable {@link ObjectPool} implementation.
- * <p>
- * When coupled with the appropriate {@link PooledObjectFactory},
- * <code>GenericObjectPool</code> provides robust pooling functionality for
- * arbitrary objects.</p>
- * <p>
- * Optionally, one may configure the pool to examine and possibly evict objects
- * as they sit idle in the pool and to ensure that a minimum number of idle
- * objects are available. This is performed by an "idle object eviction" thread,
- * which runs asynchronously. Caution should be used when configuring this
- * optional feature. Eviction runs contend with client threads for access to
- * objects in the pool, so if they run too frequently performance issues may
- * result.</p>
- * <p>
- * The pool can also be configured to detect and remove "abandoned" objects,
- * i.e. objects that have been checked out of the pool but neither used nor
- * returned before the configured
- * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}.
- * Abandoned object removal can be configured to happen when
- * <code>borrowObject</code> is invoked and the pool is close to starvation, or
- * it can be executed by the idle object evictor, or both. If pooled objects
- * implement the {@link TrackedUse} interface, their last use will be queried
- * using the <code>getLastUsed</code> method on that interface; otherwise
- * abandonment is determined by how long an object has been checked out from
- * the pool.</p>
- * <p>
- * Implementation note: To prevent possible deadlocks, care has been taken to
- * ensure that no call to a factory method will occur within a synchronization
- * block. See POOL-125 and DBCP-44 for more information.</p>
- * <p>
- * This class is intended to be thread-safe.</p>
- *
- * @see GenericKeyedObjectPool
- *
- * @param <T> Type of element pooled in this pool.
- *
- * @since 2.0
- */
-public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
-        implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {
-
-    /**
-     * Creates a new <code>GenericObjectPool</code> using defaults from
-     * {@link GenericObjectPoolConfig}.
-     *
-     * @param factory The object factory to be used to create object instances
-     *                used by this pool
-     */
-    public GenericObjectPool(final PooledObjectFactory<T> factory) {
-        this(factory, new GenericObjectPoolConfig<T>());
-    }
-
-    /**
-     * Creates a new <code>GenericObjectPool</code> using a specific
-     * configuration.
-     *
-     * @param factory   The object factory to be used to create object instances
-     *                  used by this pool
-     * @param config    The configuration to use for this pool instance. The
-     *                  configuration is used by value. Subsequent changes to
-     *                  the configuration object will not be reflected in the
-     *                  pool.
-     */
-    public GenericObjectPool(final PooledObjectFactory<T> factory,
-            final GenericObjectPoolConfig<T> config) {
-
-        super(config, ONAME_BASE, config.getJmxNamePrefix());
-
-        if (factory == null) {
-            jmxUnregister(); // tidy up
-            throw new IllegalArgumentException("factory may not be null");
-        }
-        this.factory = factory;
-
-        idleObjects = new LinkedBlockingDeque<>(config.getFairness());
-
-        setConfig(config);
-    }
-
-    /**
-     * Creates a new <code>GenericObjectPool</code> that tracks and destroys
-     * objects that are checked out, but never returned to the pool.
-     *
-     * @param factory   The object factory to be used to create object instances
-     *                  used by this pool
-     * @param config    The base pool configuration to use for this pool instance.
-     *                  The configuration is used by value. Subsequent changes to
-     *                  the configuration object will not be reflected in the
-     *                  pool.
-     * @param abandonedConfig  Configuration for abandoned object identification
-     *                         and removal.  The configuration is used by value.
-     */
-    public GenericObjectPool(final PooledObjectFactory<T> factory,
-            final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
-        this(factory, config);
-        setAbandonedConfig(abandonedConfig);
-    }
-
-    /**
-     * Returns the cap on the number of "idle" instances in the pool. If maxIdle
-     * is set too low on heavily loaded systems it is possible you will see
-     * objects being destroyed and almost immediately new objects being created.
-     * This is a result of the active threads momentarily returning objects
-     * faster than they are requesting them, causing the number of idle
-     * objects to rise above maxIdle. The best value for maxIdle for heavily
-     * loaded system will vary but the default is a good starting point.
-     *
-     * @return the maximum number of "idle" instances that can be held in the
-     *         pool or a negative value if there is no limit
-     *
-     * @see #setMaxIdle
-     */
-    @Override
-    public int getMaxIdle() {
-        return maxIdle;
-    }
-
-    /**
-     * Returns the cap on the number of "idle" instances in the pool. If maxIdle
-     * is set too low on heavily loaded systems it is possible you will see
-     * objects being destroyed and almost immediately new objects being created.
-     * This is a result of the active threads momentarily returning objects
-     * faster than they are requesting them, causing the number of idle
-     * objects to rise above maxIdle. The best value for maxIdle for heavily
-     * loaded system will vary but the default is a good starting point.
-     *
-     * @param maxIdle
-     *            The cap on the number of "idle" instances in the pool. Use a
-     *            negative value to indicate an unlimited number of idle
-     *            instances
-     *
-     * @see #getMaxIdle
-     */
-    public void setMaxIdle(final int maxIdle) {
-        this.maxIdle = maxIdle;
-    }
-
-    /**
-     * Sets the target for the minimum number of idle objects to maintain in
-     * the pool. This setting only has an effect if it is positive and
-     * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this
-     * is the case, an attempt is made to ensure that the pool has the required
-     * minimum number of instances during idle object eviction runs.
-     * <p>
-     * If the configured value of minIdle is greater than the configured value
-     * for maxIdle then the value of maxIdle will be used instead.
-     *
-     * @param minIdle
-     *            The minimum number of objects.
-     *
-     * @see #getMinIdle()
-     * @see #getMaxIdle()
-     * @see #getTimeBetweenEvictionRunsMillis()
-     */
-    public void setMinIdle(final int minIdle) {
-        this.minIdle = minIdle;
-    }
-
-    /**
-     * Returns the target for the minimum number of idle objects to maintain in
-     * the pool. This setting only has an effect if it is positive and
-     * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this
-     * is the case, an attempt is made to ensure that the pool has the required
-     * minimum number of instances during idle object eviction runs.
-     * <p>
-     * If the configured value of minIdle is greater than the configured value
-     * for maxIdle then the value of maxIdle will be used instead.
-     *
-     * @return The minimum number of objects.
-     *
-     * @see #setMinIdle(int)
-     * @see #setMaxIdle(int)
-     * @see #setTimeBetweenEvictionRunsMillis(long)
-     */
-    @Override
-    public int getMinIdle() {
-        final int maxIdleSave = getMaxIdle();
-        if (this.minIdle > maxIdleSave) {
-            return maxIdleSave;
-        }
-        return minIdle;
-    }
-
-    /**
-     * Gets whether or not abandoned object removal is configured for this pool.
-     *
-     * @return true if this pool is configured to detect and remove
-     * abandoned objects
-     */
-    @Override
-    public boolean isAbandonedConfig() {
-        return abandonedConfig != null;
-    }
-
-    /**
-     * Gets whether this pool identifies and logs any abandoned objects.
-     *
-     * @return {@code true} if abandoned object removal is configured for this
-     *         pool and removal events are to be logged otherwise {@code false}
-     *
-     * @see AbandonedConfig#getLogAbandoned()
-     */
-    @Override
-    public boolean getLogAbandoned() {
-        final AbandonedConfig ac = this.abandonedConfig;
-        return ac != null && ac.getLogAbandoned();
-    }
-
-    /**
-     * Gets whether a check is made for abandoned objects when an object is borrowed
-     * from this pool.
-     *
-     * @return {@code true} if abandoned object removal is configured to be
-     *         activated by borrowObject otherwise {@code false}
-     *
-     * @see AbandonedConfig#getRemoveAbandonedOnBorrow()
-     */
-    @Override
-    public boolean getRemoveAbandonedOnBorrow() {
-        final AbandonedConfig ac = this.abandonedConfig;
-        return ac != null && ac.getRemoveAbandonedOnBorrow();
-    }
-
-    /**
-     * Gets whether a check is made for abandoned objects when the evictor runs.
-     *
-     * @return {@code true} if abandoned object removal is configured to be
-     *         activated when the evictor runs otherwise {@code false}
-     *
-     * @see AbandonedConfig#getRemoveAbandonedOnMaintenance()
-     */
-    @Override
-    public boolean getRemoveAbandonedOnMaintenance() {
-        final AbandonedConfig ac = this.abandonedConfig;
-        return ac != null && ac.getRemoveAbandonedOnMaintenance();
-    }
-
-    /**
-     * Obtains the timeout before which an object will be considered to be
-     * abandoned by this pool.
-     *
-     * @return The abandoned object timeout in seconds if abandoned object
-     *         removal is configured for this pool; Integer.MAX_VALUE otherwise.
-     *
-     * @see AbandonedConfig#getRemoveAbandonedTimeout()
-     */
-    @Override
-    public int getRemoveAbandonedTimeout() {
-        final AbandonedConfig ac = this.abandonedConfig;
-        return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE;
-    }
-
-
-    /**
-     * Sets the base pool configuration.
-     *
-     * @param conf the new configuration to use. This is used by value.
-     *
-     * @see GenericObjectPoolConfig
-     */
-    public void setConfig(final GenericObjectPoolConfig<T> conf) {
-        super.setConfig(conf);
-        setMaxIdle(conf.getMaxIdle());
-        setMinIdle(conf.getMinIdle());
-        setMaxTotal(conf.getMaxTotal());
-    }
-
-    /**
-     * Sets the abandoned object removal configuration.
-     *
-     * @param abandonedConfig the new configuration to use. This is used by value.
-     *
-     * @see AbandonedConfig
-     */
-    public void setAbandonedConfig(final AbandonedConfig abandonedConfig) {
-        if (abandonedConfig == null) {
-            this.abandonedConfig = null;
-        } else {
-            this.abandonedConfig = new AbandonedConfig();
-            this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned());
-            this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter());
-            this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow());
-            this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance());
-            this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeout());
-            this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking());
-            this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace());
-        }
-    }
-
-    /**
-     * Obtains a reference to the factory used to create, destroy and validate
-     * the objects used by this pool.
-     *
-     * @return the factory
-     */
-    public PooledObjectFactory<T> getFactory() {
-        return factory;
-    }
-
-    /**
-     * Equivalent to <code>{@link #borrowObject(long)
-     * borrowObject}({@link #getMaxWaitMillis()})</code>.
-     * <p>
-     * {@inheritDoc}
-     */
-    @Override
-    public T borrowObject() throws Exception {
-        return borrowObject(getMaxWaitMillis());
-    }
-
-    /**
-     * Borrows an object from the pool using the specific waiting time which only
-     * applies if {@link #getBlockWhenExhausted()} is true.
-     * <p>
-     * If there is one or more idle instance available in the pool, then an
-     * idle instance will be selected based on the value of {@link #getLifo()},
-     * activated and returned. If activation fails, or {@link #getTestOnBorrow()
-     * testOnBorrow} is set to <code>true</code> and validation fails, the
-     * instance is destroyed and the next available instance is examined. This
-     * continues until either a valid instance is returned or there are no more
-     * idle instances available.
-     * <p>
-     * If there are no idle instances available in the pool, behavior depends on
-     * the {@link #getMaxTotal() maxTotal}, (if applicable)
-     * {@link #getBlockWhenExhausted()} and the value passed in to the
-     * <code>borrowMaxWaitMillis</code> parameter. If the number of instances
-     * checked out from the pool is less than <code>maxTotal,</code> a new
-     * instance is created, activated and (if applicable) validated and returned
-     * to the caller. If validation fails, a <code>NoSuchElementException</code>
-     * is thrown.
-     * <p>
-     * If the pool is exhausted (no available idle instances and no capacity to
-     * create new ones), this method will either block (if
-     * {@link #getBlockWhenExhausted()} is true) or throw a
-     * <code>NoSuchElementException</code> (if
-     * {@link #getBlockWhenExhausted()} is false). The length of time that this
-     * method will block when {@link #getBlockWhenExhausted()} is true is
-     * determined by the value passed in to the <code>borrowMaxWaitMillis</code>
-     * parameter.
-     * <p>
-     * When the pool is exhausted, multiple calling threads may be
-     * simultaneously blocked waiting for instances to become available. A
-     * "fairness" algorithm has been implemented to ensure that threads receive
-     * available instances in request arrival order.
-     *
-     * @param borrowMaxWaitMillis The time to wait in milliseconds for an object
-     *                            to become available
-     *
-     * @return object instance from the pool
-     *
-     * @throws NoSuchElementException if an instance cannot be returned
-     *
-     * @throws Exception if an object instance cannot be returned due to an
-     *                   error
-     */
-    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
-        assertOpen();
-
-        final AbandonedConfig ac = this.abandonedConfig;
-        if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
-                (getNumIdle() < 2) &&
-                (getNumActive() > getMaxTotal() - 3) ) {
-            removeAbandoned(ac);
-        }
-
-        PooledObject<T> p = null;
-
-        // Get local copy of current config so it is consistent for entire
-        // method execution
-        final boolean blockWhenExhausted = getBlockWhenExhausted();
-
-        boolean create;
-        final long waitTime = System.currentTimeMillis();
-
-        while (p == null) {
-            create = false;
-            p = idleObjects.pollFirst();
-            if (p == null) {
-                p = create();
-                if (p != null) {
-                    create = true;
-                }
-            }
-            if (blockWhenExhausted) {
-                if (p == null) {
-                    if (borrowMaxWaitMillis < 0) {
-                        p = idleObjects.takeFirst();
-                    } else {
-                        p = idleObjects.pollFirst(borrowMaxWaitMillis,
-                                TimeUnit.MILLISECONDS);
-                    }
-                }
-                if (p == null) {
-                    throw new NoSuchElementException(
-                            "Timeout waiting for idle object");
-                }
-            } else {
-                if (p == null) {
-                    throw new NoSuchElementException("Pool exhausted");
-                }
-            }
-            if (!p.allocate()) {
-                p = null;
-            }
-
-            if (p != null) {
-                try {
-                    factory.activateObject(p);
-                } catch (final Exception e) {
-                    try {
-                        destroy(p);
-                    } catch (final Exception e1) {
-                        // Ignore - activation failure is more important
-                    }
-                    p = null;
-                    if (create) {
-                        final NoSuchElementException nsee = new NoSuchElementException(
-                                "Unable to activate object");
-                        nsee.initCause(e);
-                        throw nsee;
-                    }
-                }
-                if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
-                    boolean validate = false;
-                    Throwable validationThrowable = null;
-                    try {
-                        validate = factory.validateObject(p);
-                    } catch (final Throwable t) {
-                        PoolUtils.checkRethrow(t);
-                        validationThrowable = t;
-                    }
-                    if (!validate) {
-                        try {
-                            destroy(p);
-                            destroyedByBorrowValidationCount.incrementAndGet();
-                        } catch (final Exception e) {
-                            // Ignore - validation failure is more important
-                        }
-                        p = null;
-                        if (create) {
-                            final NoSuchElementException nsee = new NoSuchElementException(
-                                    "Unable to validate object");
-                            nsee.initCause(validationThrowable);
-                            throw nsee;
-                        }
-                    }
-                }
-            }
-        }
-
-        updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
-
-        return p.getObject();
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * If {@link #getMaxIdle() maxIdle} is set to a positive value and the
-     * number of idle instances has reached this value, the returning instance
-     * is destroyed.
-     * <p>
-     * If {@link #getTestOnReturn() testOnReturn} == true, the returning
-     * instance is validated before being returned to the idle instance pool. In
-     * this case, if validation fails, the instance is destroyed.
-     * <p>
-     * Exceptions encountered destroying objects for any reason are swallowed
-     * but notified via a {@link SwallowedExceptionListener}.
-     */
-    @Override
-    public void returnObject(final T obj) {
-        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
-
-        if (p == null) {
-            if (!isAbandonedConfig()) {
-                throw new IllegalStateException(
-                        "Returned object not currently part of this pool");
-            }
-            return; // Object was abandoned and removed
-        }
-
-        markReturningState(p);
-
-        final long activeTime = p.getActiveTimeMillis();
-
-        if (getTestOnReturn() && !factory.validateObject(p)) {
-            try {
-                destroy(p);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            try {
-                ensureIdle(1, false);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            updateStatsReturn(activeTime);
-            return;
-        }
-
-        try {
-            factory.passivateObject(p);
-        } catch (final Exception e1) {
-            swallowException(e1);
-            try {
-                destroy(p);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            try {
-                ensureIdle(1, false);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            updateStatsReturn(activeTime);
-            return;
-        }
-
-        if (!p.deallocate()) {
-            throw new IllegalStateException(
-                    "Object has already been returned to this pool or is invalid");
-        }
-
-        final int maxIdleSave = getMaxIdle();
-        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
-            try {
-                destroy(p);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-        } else {
-            if (getLifo()) {
-                idleObjects.addFirst(p);
-            } else {
-                idleObjects.addLast(p);
-            }
-            if (isClosed()) {
-                // Pool closed while object was being added to idle objects.
-                // Make sure the returned object is destroyed rather than left
-                // in the idle object pool (which would effectively be a leak)
-                clear();
-            }
-        }
-        updateStatsReturn(activeTime);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Activation of this method decrements the active count and attempts to
-     * destroy the instance.
-     *
-     * @throws Exception             if an exception occurs destroying the
-     *                               object
-     * @throws IllegalStateException if obj does not belong to this pool
-     */
-    @Override
-    public void invalidateObject(final T obj) throws Exception {
-        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
-        if (p == null) {
-            if (isAbandonedConfig()) {
-                return;
-            }
-            throw new IllegalStateException(
-                    "Invalidated object not currently part of this pool");
-        }
-        synchronized (p) {
-            if (p.getState() != PooledObjectState.INVALID) {
-                destroy(p);
-            }
-        }
-        ensureIdle(1, false);
-    }
-
-    /**
-     * Clears any objects sitting idle in the pool by removing them from the
-     * idle instance pool and then invoking the configured
-     * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each
-     * idle instance.
-     * <p>
-     * Implementation notes:
-     * <ul>
-     * <li>This method does not destroy or effect in any way instances that are
-     * checked out of the pool when it is invoked.</li>
-     * <li>Invoking this method does not prevent objects being returned to the
-     * idle instance pool, even during its execution. Additional instances may
-     * be returned while removed items are being destroyed.</li>
-     * <li>Exceptions encountered destroying idle instances are swallowed
-     * but notified via a {@link SwallowedExceptionListener}.</li>
-     * </ul>
-     */
-    @Override
-    public void clear() {
-        PooledObject<T> p = idleObjects.poll();
-
-        while (p != null) {
-            try {
-                destroy(p);
-            } catch (final Exception e) {
-                swallowException(e);
-            }
-            p = idleObjects.poll();
-        }
-    }
-
-    @Override
-    public int getNumActive() {
-        return allObjects.size() - idleObjects.size();
-    }
-
-    @Override
-    public int getNumIdle() {
-        return idleObjects.size();
-    }
-
-    /**
-     * Closes the pool. Once the pool is closed, {@link #borrowObject()} will
-     * fail with IllegalStateException, but {@link #returnObject(Object)} and
-     * {@link #invalidateObject(Object)} will continue to work, with returned
-     * objects destroyed on return.
-     * <p>
-     * Destroys idle instances in the pool by invoking {@link #clear()}.
-     */
-    @Override
-    public void close() {
-        if (isClosed()) {
-            return;
-        }
-
-        synchronized (closeLock) {
-            if (isClosed()) {
-                return;
-            }
-
-            // Stop the evictor before the pool is closed since evict() calls
-            // assertOpen()
-            stopEvitor();
-
-            closed = true;
-            // This clear removes any idle objects
-            clear();
-
-            jmxUnregister();
-
-            // Release any threads that were waiting for an object
-            idleObjects.interuptTakeWaiters();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Successive activations of this method examine objects in sequence,
-     * cycling through objects in oldest-to-youngest order.
-     */
-    @Override
-    public void evict() throws Exception {
-        assertOpen();
-
-        if (idleObjects.size() > 0) {
-
-            PooledObject<T> underTest = null;
-            final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
-
-            synchronized (evictionLock) {
-                final EvictionConfig evictionConfig = new EvictionConfig(
-                        getMinEvictableIdleTimeMillis(),
-                        getSoftMinEvictableIdleTimeMillis(),
-                        getMinIdle());
-
-                final boolean testWhileIdle = getTestWhileIdle();
-
-                for (int i = 0, m = getNumTests(); i < m; i++) {
-                    if (evictionIterator == null || !evictionIterator.hasNext()) {
-                        evictionIterator = new EvictionIterator(idleObjects);
-                    }
-                    if (!evictionIterator.hasNext()) {
-                        // Pool exhausted, nothing to do here
-                        return;
-                    }
-
-                    try {
-                        underTest = evictionIterator.next();
-                    } catch (final NoSuchElementException nsee) {
-                        // Object was borrowed in another thread
-                        // Don't count this as an eviction test so reduce i;
-                        i--;
-                        evictionIterator = null;
-                        continue;
-                    }
-
-                    if (!underTest.startEvictionTest()) {
-                        // Object was borrowed in another thread
-                        // Don't count this as an eviction test so reduce i;
-                        i--;
-                        continue;
-                    }
-
-                    // User provided eviction policy could throw all sorts of
-                    // crazy exceptions. Protect against such an exception
-                    // killing the eviction thread.
-                    boolean evict;
-                    try {
-                        evict = evictionPolicy.evict(evictionConfig, underTest,
-                                idleObjects.size());
-                    } catch (final Throwable t) {
-                        // Slightly convoluted as SwallowedExceptionListener
-                        // uses Exception rather than Throwable
-                        PoolUtils.checkRethrow(t);
-                        swallowException(new Exception(t));
-                        // Don't evict on error conditions
-                        evict = false;
-                    }
-
-                    if (evict) {
-                        destroy(underTest);
-                        destroyedByEvictorCount.incrementAndGet();
-                    } else {
-                        if (testWhileIdle) {
-                            boolean active = false;
-                            try {
-                                factory.activateObject(underTest);
-                                active = true;
-                            } catch (final Exception e) {
-                                destroy(underTest);
-                                destroyedByEvictorCount.incrementAndGet();
-                            }
-                            if (active) {
-                                if (!factory.validateObject(underTest)) {
-                                    destroy(underTest);
-                                    destroyedByEvictorCount.incrementAndGet();
-                                } else {
-                                    try {
-                                        factory.passivateObject(underTest);
-                                    } catch (final Exception e) {
-                                        destroy(underTest);
-                                        destroyedByEvictorCount.incrementAndGet();
-                                    }
-                                }
-                            }
-                        }
-                        if (!underTest.endEvictionTest(idleObjects)) {
-                            // TODO - May need to add code here once additional
-                            // states are used
-                        }
-                    }
-                }
-            }
-        }
-        final AbandonedConfig ac = this.abandonedConfig;
-        if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
-            removeAbandoned(ac);
-        }
-    }
-
-    /**
-     * Tries to ensure that {@link #getMinIdle()} idle instances are available
-     * in the pool.
-     *
-     * @throws Exception If the associated factory throws an exception
-     * @since 2.4
-     */
-    public void preparePool() throws Exception {
-        if (getMinIdle() < 1) {
-            return;
-        }
-        ensureMinIdle();
-    }
-
-    /**
-     * Attempts to create a new wrapped pooled object.
-     * <p>
-     * If there are {@link #getMaxTotal()} objects already in circulation
-     * or in process of being created, this method returns null.
-     *
-     * @return The new wrapped pooled object
-     *
-     * @throws Exception if the object factory's {@code makeObject} fails
-     */
-    private PooledObject<T> create() throws Exception {
-        int localMaxTotal = getMaxTotal();
-        // This simplifies the code later in this method
-        if (localMaxTotal < 0) {
-            localMaxTotal = Integer.MAX_VALUE;
-        }
-
-        final long localStartTimeMillis = System.currentTimeMillis();
-        final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0);
-
-        // Flag that indicates if create should:
-        // - TRUE:  call the factory to create an object
-        // - FALSE: return null
-        // - null:  loop and re-test the condition that determines whether to
-        //          call the factory
-        Boolean create = null;
-        while (create == null) {
-            synchronized (makeObjectCountLock) {
-                final long newCreateCount = createCount.incrementAndGet();
-                if (newCreateCount > localMaxTotal) {
-                    // The pool is currently at capacity or in the process of
-                    // making enough new objects to take it to capacity.
-                    createCount.decrementAndGet();
-                    if (makeObjectCount == 0) {
-                        // There are no makeObject() calls in progress so the
-                        // pool is at capacity. Do not attempt to create a new
-                        // object. Return and wait for an object to be returned
-                        create = Boolean.FALSE;
-                    } else {
-                        // There are makeObject() calls in progress that might
-                        // bring the pool to capacity. Those calls might also
-                        // fail so wait until they complete and then re-test if
-                        // the pool is at capacity or not.
-                        makeObjectCountLock.wait(localMaxWaitTimeMillis);
-                    }
-                } else {
-                    // The pool is not at capacity. Create a new object.
-                    makeObjectCount++;
-                    create = Boolean.TRUE;
-                }
-            }
-
-            // Do not block more if maxWaitTimeMillis is set.
-            if (create == null &&
-                (localMaxWaitTimeMillis > 0 &&
-                 System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) {
-                create = Boolean.FALSE;
-            }
-        }
-
-        if (!create.booleanValue()) {
-            return null;
-        }
-
-        final PooledObject<T> p;
-        try {
-            p = factory.makeObject();
-        } catch (final Throwable e) {
-            createCount.decrementAndGet();
-            throw e;
-        } finally {
-            synchronized (makeObjectCountLock) {
-                makeObjectCount--;
-                makeObjectCountLock.notifyAll();
-            }
-        }
-
-        final AbandonedConfig ac = this.abandonedConfig;
-        if (ac != null && ac.getLogAbandoned()) {
-            p.setLogAbandoned(true);
-            // TODO: in 3.0, this can use the method defined on PooledObject
-            if (p instanceof DefaultPooledObject<?>) {
-                ((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace());
-            }
-        }
-
-        createdCount.incrementAndGet();
-        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
-        return p;
-    }
-
-    /**
-     * Destroys a wrapped pooled object.
-     *
-     * @param toDestroy The wrapped pooled object to destroy
-     *
-     * @throws Exception If the factory fails to destroy the pooled object
-     *                   cleanly
-     */
-    private void destroy(final PooledObject<T> toDestroy) throws Exception {
-        toDestroy.invalidate();
-        idleObjects.remove(toDestroy);
-        allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
-        try {
-            factory.destroyObject(toDestroy);
-        } finally {
-            destroyedCount.incrementAndGet();
-            createCount.decrementAndGet();
-        }
-
-        if (idleObjects.isEmpty() && idleObjects.hasTakeWaiters()) {
-            // POOL-356.
-            // In case there are already threads waiting on something in the pool
-            // (e.g. idleObjects.takeFirst(); then we need to provide them a fresh instance.
-            // Otherwise they will be stuck forever (or until timeout)
-            final PooledObject<T> freshPooled = create();
-            idleObjects.put(freshPooled);
-        }
-    }
-
-    @Override
-    void ensureMinIdle() throws Exception {
-        ensureIdle(getMinIdle(), true);
-    }
-
-    /**
-     * Tries to ensure that {@code idleCount} idle instances exist in the pool.
-     * <p>
-     * Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount}
-     * or the total number of objects (idle, checked out, or being created) reaches
-     * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless
-     * there are threads waiting to check out instances from the pool.
-     *
-     * @param idleCount the number of idle instances desired
-     * @param always true means create instances even if the pool has no threads waiting
-     * @throws Exception if the factory's makeObject throws
-     */
-    private void ensureIdle(final int idleCount, final boolean always) throws Exception {
-        if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
-            return;
-        }
-
-        while (idleObjects.size() < idleCount) {
-            final PooledObject<T> p = create();
-            if (p == null) {
-                // Can't create objects, no reason to think another call to
-                // create will work. Give up.
-                break;
-            }
-            if (getLifo()) {
-                idleObjects.addFirst(p);
-            } else {
-                idleObjects.addLast(p);
-            }
-        }
-        if (isClosed()) {
-            // Pool closed while object was being added to idle objects.
-            // Make sure the returned object is destroyed rather than left
-            // in the idle object pool (which would effectively be a leak)
-            clear();
-        }
-    }
-
-    /**
-     * Creates an object, and place it into the pool. addObject() is useful for
-     * "pre-loading" a pool with idle objects.
-     * <p>
-     * If there is no capacity available to add to the pool, this is a no-op
-     * (no exception, no impact to the pool). </p>
-     */
-    @Override
-    public void addObject() throws Exception {
-        assertOpen();
-        if (factory == null) {
-            throw new IllegalStateException(
-                    "Cannot add objects without a factory.");
-        }
-        final PooledObject<T> p = create();
-        addIdleObject(p);
-    }
-
-    /**
-     * Adds the provided wrapped pooled object to the set of idle objects for
-     * this pool. The object must already be part of the pool.  If {@code p}
-     * is null, this is a no-op (no exception, but no impact on the pool).
-     *
-     * @param p The object to make idle
-     *
-     * @throws Exception If the factory fails to passivate the object
-     */
-    private void addIdleObject(final PooledObject<T> p) throws Exception {
-        if (p != null) {
-            factory.passivateObject(p);
-            if (getLifo()) {
-                idleObjects.addFirst(p);
-            } else {
-                idleObjects.addLast(p);
-            }
-        }
-    }
-
-    /**
-     * Calculates the number of objects to test in a run of the idle object
-     * evictor.
-     *
-     * @return The number of objects to test for validity
-     */
-    private int getNumTests() {
-        final int numTestsPerEvictionRun = getNumTestsPerEvictionRun();
-        if (numTestsPerEvictionRun >= 0) {
-            return Math.min(numTestsPerEvictionRun, idleObjects.size());
-        }
-        return (int) (Math.ceil(idleObjects.size() /
-                Math.abs((double) numTestsPerEvictionRun)));
-    }
-
-    /**
-     * Recovers abandoned objects which have been checked out but
-     * not used since longer than the removeAbandonedTimeout.
-     *
-     * @param ac The configuration to use to identify abandoned objects
-     */
-    private void removeAbandoned(final AbandonedConfig ac) {
-        // Generate a list of abandoned objects to remove
-        final long now = System.currentTimeMillis();
-        final long timeout =
-                now - (ac.getRemoveAbandonedTimeout() * 1000L);
-        final ArrayList<PooledObject<T>> remove = new ArrayList<>();
-        final Iterator<PooledObject<T>> it = allObjects.values().iterator();
-        while (it.hasNext()) {
-            final PooledObject<T> pooledObject = it.next();
-            synchronized (pooledObject) {
-                if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
-                        pooledObject.getLastUsedTime() <= timeout) {
-                    pooledObject.markAbandoned();
-                    remove.add(pooledObject);
-                }
-            }
-        }
-
-        // Now remove the abandoned objects
-        final Iterator<PooledObject<T>> itr = remove.iterator();
-        while (itr.hasNext()) {
-            final PooledObject<T> pooledObject = itr.next();
-            if (ac.getLogAbandoned()) {
-                pooledObject.printStackTrace(ac.getLogWriter());
-            }
-            try {
-                invalidateObject(pooledObject.getObject());
-            } catch (final Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-
-    //--- Usage tracking support -----------------------------------------------
-
-    @Override
-    public void use(final T pooledObject) {
-        final AbandonedConfig ac = this.abandonedConfig;
-        if (ac != null && ac.getUseUsageTracking()) {
-            final PooledObject<T> wrapper = allObjects.get(new IdentityWrapper<>(pooledObject));
-            wrapper.use();
-        }
-    }
-
-
-    //--- JMX support ----------------------------------------------------------
-
-    private volatile String factoryType = null;
-
-    /**
-     * Returns an estimate of the number of threads currently blocked waiting for
-     * an object from the pool. This is intended for monitoring only, not for
-     * synchronization control.
-     *
-     * @return The estimate of the number of threads currently blocked waiting
-     *         for an object from the pool
-     */
-    @Override
-    public int getNumWaiters() {
-        if (getBlockWhenExhausted()) {
-            return idleObjects.getTakeQueueLength();
-        }
-        return 0;
-    }
-
-    /**
-     * Returns the type - including the specific type rather than the generic -
-     * of the factory.
-     *
-     * @return A string representation of the factory type
-     */
-    @Override
-    public String getFactoryType() {
-        // Not thread safe. Accept that there may be multiple evaluations.
-        if (factoryType == null) {
-            final StringBuilder result = new StringBuilder();
-            result.append(factory.getClass().getName());
-            result.append('<');
-            final Class<?> pooledObjectType =
-                    PoolImplUtils.getFactoryType(factory.getClass());
-            result.append(pooledObjectType.getName());
-            result.append('>');
-            factoryType = result.toString();
-        }
-        return factoryType;
-    }
-
-    /**
-     * Provides information on all the objects in the pool, both idle (waiting
-     * to be borrowed) and active (currently borrowed).
-     * <p>
-     * Note: This is named listAllObjects so it is presented as an operation via
-     * JMX. That means it won't be invoked unless the explicitly requested
-     * whereas all attributes will be automatically requested when viewing the
-     * attributes for an object in a tool like JConsole.
-     *
-     * @return Information grouped on all the objects in the pool
-     */
-    @Override
-    public Set<DefaultPooledObjectInfo> listAllObjects() {
-        final Set<DefaultPooledObjectInfo> result =
-                new HashSet<>(allObjects.size());
-        for (final PooledObject<T> p : allObjects.values()) {
-            result.add(new DefaultPooledObjectInfo(p));
-        }
-        return result;
-    }
-
-    // --- configuration attributes --------------------------------------------
-
-    private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
-    private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
-    private final PooledObjectFactory<T> factory;
-
-
-    // --- internal attributes -------------------------------------------------
-
-    /*
-     * All of the objects currently associated with this pool in any state. It
-     * excludes objects that have been destroyed. The size of
-     * {@link #allObjects} will always be less than or equal to {@link
-     * #_maxActive}. Map keys are pooled objects, values are the PooledObject
-     * wrappers used internally by the pool.
-     */
-    private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
-        new ConcurrentHashMap<>();
-    /*
-     * The combined count of the currently created objects and those in the
-     * process of being created. Under load, it may exceed {@link #_maxActive}
-     * if multiple threads try and create a new object at the same time but
-     * {@link #create()} will ensure that there are never more than
-     * {@link #_maxActive} objects created at any one time.
-     */
-    private final AtomicLong createCount = new AtomicLong(0);
-    private long makeObjectCount = 0;
-    private final Object makeObjectCountLock = new Object();
-    private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
-
-    // JMX specific attributes
-    private static final String ONAME_BASE =
-        "org.apache.commons.pool2:type=GenericObjectPool,name=";
-
-    // Additional configuration properties for abandoned object tracking
-    private volatile AbandonedConfig abandonedConfig = null;
-
-    @Override
-    protected void toStringAppendFields(final StringBuilder builder) {
-        super.toStringAppendFields(builder);
-        builder.append(", factoryType=");
-        builder.append(factoryType);
-        builder.append(", maxIdle=");
-        builder.append(maxIdle);
-        builder.append(", minIdle=");
-        builder.append(minIdle);
-        builder.append(", factory=");
-        builder.append(factory);
-        builder.append(", allObjects=");
-        builder.append(allObjects);
-        builder.append(", createCount=");
-        builder.append(createCount);
-        builder.append(", idleObjects=");
-        builder.append(idleObjects);
-        builder.append(", abandonedConfig=");
-        builder.append(abandonedConfig);
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.pool2.impl;
+
+import org.apache.commons.pool2.ObjectPool;
+import org.apache.commons.pool2.PoolUtils;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.PooledObjectState;
+import org.apache.commons.pool2.SwallowedExceptionListener;
+import org.apache.commons.pool2.TrackedUse;
+import org.apache.commons.pool2.UsageTracking;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A configurable {@link ObjectPool} implementation.
+ * <p>
+ * When coupled with the appropriate {@link PooledObjectFactory},
+ * <code>GenericObjectPool</code> provides robust pooling functionality for
+ * arbitrary objects.</p>
+ * <p>
+ * Optionally, one may configure the pool to examine and possibly evict objects
+ * as they sit idle in the pool and to ensure that a minimum number of idle
+ * objects are available. This is performed by an "idle object eviction" thread,
+ * which runs asynchronously. Caution should be used when configuring this
+ * optional feature. Eviction runs contend with client threads for access to
+ * objects in the pool, so if they run too frequently performance issues may
+ * result.</p>
+ * <p>
+ * The pool can also be configured to detect and remove "abandoned" objects,
+ * i.e. objects that have been checked out of the pool but neither used nor
+ * returned before the configured
+ * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}.
+ * Abandoned object removal can be configured to happen when
+ * <code>borrowObject</code> is invoked and the pool is close to starvation, or
+ * it can be executed by the idle object evictor, or both. If pooled objects
+ * implement the {@link TrackedUse} interface, their last use will be queried
+ * using the <code>getLastUsed</code> method on that interface; otherwise
+ * abandonment is determined by how long an object has been checked out from
+ * the pool.</p>
+ * <p>
+ * Implementation note: To prevent possible deadlocks, care has been taken to
+ * ensure that no call to a factory method will occur within a synchronization
+ * block. See POOL-125 and DBCP-44 for more information.</p>
+ * <p>
+ * This class is intended to be thread-safe.</p>
+ *
+ * @see GenericKeyedObjectPool
+ *
+ * @param <T> Type of element pooled in this pool.
+ *
+ * @since 2.0
+ */
+public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
+        implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {
+
+    /**
+     * Creates a new <code>GenericObjectPool</code> using defaults from
+     * {@link GenericObjectPoolConfig}.
+     *
+     * @param factory The object factory to be used to create object instances
+     *                used by this pool
+     */
+    public GenericObjectPool(final PooledObjectFactory<T> factory) {
+        this(factory, new GenericObjectPoolConfig<T>());
+    }
+
+    /**
+     * Creates a new <code>GenericObjectPool</code> using a specific
+     * configuration.
+     *
+     * @param factory   The object factory to be used to create object instances
+     *                  used by this pool
+     * @param config    The configuration to use for this pool instance. The
+     *                  configuration is used by value. Subsequent changes to
+     *                  the configuration object will not be reflected in the
+     *                  pool.
+     */
+    public GenericObjectPool(final PooledObjectFactory<T> factory,
+            final GenericObjectPoolConfig<T> config) {
+
+        super(config, ONAME_BASE, config.getJmxNamePrefix());
+
+        if (factory == null) {
+            jmxUnregister(); // tidy up
+            throw new IllegalArgumentException("factory may not be null");
+        }
+        this.factory = factory;
+
+        idleObjects = new LinkedBlockingDeque<>(config.getFairness());
+
+        setConfig(config);
+    }
+
+    /**
+     * Creates a new <code>GenericObjectPool</code> that tracks and destroys
+     * objects that are checked out, but never returned to the pool.
+     *
+     * @param factory   The object factory to be used to create object instances
+     *                  used by this pool
+     * @param config    The base pool configuration to use for this pool instance.
+     *                  The configuration is used by value. Subsequent changes to
+     *                  the configuration object will not be reflected in the
+     *                  pool.
+     * @param abandonedConfig  Configuration for abandoned object identification
+     *                         and removal.  The configuration is used by value.
+     */
+    public GenericObjectPool(final PooledObjectFactory<T> factory,
+            final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
+        this(factory, config);
+        setAbandonedConfig(abandonedConfig);
+    }
+
+    /**
+     * Returns the cap on the number of "idle" instances in the pool. If maxIdle
+     * is set too low on heavily loaded systems it is possible you will see
+     * objects being destroyed and almost immediately new objects being created.
+     * This is a result of the active threads momentarily returning objects
+     * faster than they are requesting them, causing the number of idle
+     * objects to rise above maxIdle. The best value for maxIdle for heavily
+     * loaded system will vary but the default is a good starting point.
+     *
+     * @return the maximum number of "idle" instances that can be held in the
+     *         pool or a negative value if there is no limit
+     *
+     * @see #setMaxIdle
+     */
+    @Override
+    public int getMaxIdle() {
+        return maxIdle;
+    }
+
+    /**
+     * Returns the cap on the number of "idle" instances in the pool. If maxIdle
+     * is set too low on heavily loaded systems it is possible you will see
+     * objects being destroyed and almost immediately new objects being created.
+     * This is a result of the active threads momentarily returning objects
+     * faster than they are requesting them, causing the number of idle
+     * objects to rise above maxIdle. The best value for maxIdle for heavily
+     * loaded system will vary but the default is a good starting point.
+     *
+     * @param maxIdle
+     *            The cap on the number of "idle" instances in the pool. Use a
+     *            negative value to indicate an unlimited number of idle
+     *            instances
+     *
+     * @see #getMaxIdle
+     */
+    public void setMaxIdle(final int maxIdle) {
+        this.maxIdle = maxIdle;
+    }
+
+    /**
+     * Sets the target for the minimum number of idle objects to maintain in
+     * the pool. This setting only has an effect if it is positive and
+     * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this
+     * is the case, an attempt is made to ensure that the pool has the required
+     * minimum number of instances during idle object eviction runs.
+     * <p>
+     * If the configured value of minIdle is greater than the configured value
+     * for maxIdle then the value of maxIdle will be used instead.
+     * </p>
+     *
+     * @param minIdle
+     *            The minimum number of objects.
+     *
+     * @see #getMinIdle()
+     * @see #getMaxIdle()
+     * @see #getTimeBetweenEvictionRunsMillis()
+     */
+    public void setMinIdle(final int minIdle) {
+        this.minIdle = minIdle;
+    }
+
+    /**
+     * Returns the target for the minimum number of idle objects to maintain in
+     * the pool. This setting only has an effect if it is positive and
+     * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this
+     * is the case, an attempt is made to ensure that the pool has the required
+     * minimum number of instances during idle object eviction runs.
+     * <p>
+     * If the configured value of minIdle is greater than the configured value
+     * for maxIdle then the value of maxIdle will be used instead.
+     * </p>
+     *
+     * @return The minimum number of objects.
+     *
+     * @see #setMinIdle(int)
+     * @see #setMaxIdle(int)
+     * @see #setTimeBetweenEvictionRunsMillis(long)
+     */
+    @Override
+    public int getMinIdle() {
+        final int maxIdleSave = getMaxIdle();
+        if (this.minIdle > maxIdleSave) {
+            return maxIdleSave;
+        }
+        return minIdle;
+    }
+
+    /**
+     * Gets whether or not abandoned object removal is configured for this pool.
+     *
+     * @return true if this pool is configured to detect and remove
+     * abandoned objects
+     */
+    @Override
+    public boolean isAbandonedConfig() {
+        return abandonedConfig != null;
+    }
+
+    /**
+     * Gets whether this pool identifies and logs any abandoned objects.
+     *
+     * @return {@code true} if abandoned object removal is configured for this
+     *         pool and removal events are to be logged otherwise {@code false}
+     *
+     * @see AbandonedConfig#getLogAbandoned()
+     */
+    @Override
+    public boolean getLogAbandoned() {
+        final AbandonedConfig ac = this.abandonedConfig;
+        return ac != null && ac.getLogAbandoned();
+    }
+
+    /**
+     * Gets whether a check is made for abandoned objects when an object is borrowed
+     * from this pool.
+     *
+     * @return {@code true} if abandoned object removal is configured to be
+     *         activated by borrowObject otherwise {@code false}
+     *
+     * @see AbandonedConfig#getRemoveAbandonedOnBorrow()
+     */
+    @Override
+    public boolean getRemoveAbandonedOnBorrow() {
+        final AbandonedConfig ac = this.abandonedConfig;
+        return ac != null && ac.getRemoveAbandonedOnBorrow();
+    }
+
+    /**
+     * Gets whether a check is made for abandoned objects when the evictor runs.
+     *
+     * @return {@code true} if abandoned object removal is configured to be
+     *         activated when the evictor runs otherwise {@code false}
+     *
+     * @see AbandonedConfig#getRemoveAbandonedOnMaintenance()
+     */
+    @Override
+    public boolean getRemoveAbandonedOnMaintenance() {
+        final AbandonedConfig ac = this.abandonedConfig;
+        return ac != null && ac.getRemoveAbandonedOnMaintenance();
+    }
+
+    /**
+     * Obtains the timeout before which an object will be considered to be
+     * abandoned by this pool.
+     *
+     * @return The abandoned object timeout in seconds if abandoned object
+     *         removal is configured for this pool; Integer.MAX_VALUE otherwise.
+     *
+     * @see AbandonedConfig#getRemoveAbandonedTimeout()
+     */
+    @Override
+    public int getRemoveAbandonedTimeout() {
+        final AbandonedConfig ac = this.abandonedConfig;
+        return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE;
+    }
+
+
+    /**
+     * Sets the base pool configuration.
+     *
+     * @param conf the new configuration to use. This is used by value.
+     *
+     * @see GenericObjectPoolConfig
+     */
+    public void setConfig(final GenericObjectPoolConfig<T> conf) {
+        super.setConfig(conf);
+        setMaxIdle(conf.getMaxIdle());
+        setMinIdle(conf.getMinIdle());
+        setMaxTotal(conf.getMaxTotal());
+    }
+
+    /**
+     * Sets the abandoned object removal configuration.
+     *
+     * @param abandonedConfig the new configuration to use. This is used by value.
+     *
+     * @see AbandonedConfig
+     */
+    public void setAbandonedConfig(final AbandonedConfig abandonedConfig) {
+        if (abandonedConfig == null) {
+            this.abandonedConfig = null;
+        } else {
+            this.abandonedConfig = new AbandonedConfig();
+            this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned());
+            this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter());
+            this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow());
+            this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance());
+            this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeout());
+            this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking());
+            this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace());
+        }
+    }
+
+    /**
+     * Obtains a reference to the factory used to create, destroy and validate
+     * the objects used by this pool.
+     *
+     * @return the factory
+     */
+    public PooledObjectFactory<T> getFactory() {
+        return factory;
+    }
+
+    /**
+     * Equivalent to <code>{@link #borrowObject(long)
+     * borrowObject}({@link #getMaxWaitMillis()})</code>.
+     * <p>
+     * {@inheritDoc}
+     * </p>
+     */
+    @Override
+    public T borrowObject() throws Exception {
+        return borrowObject(getMaxWaitMillis());
+    }
+
+    /**
+     * Borrows an object from the pool using the specific waiting time which only
+     * applies if {@link #getBlockWhenExhausted()} is true.
+     * <p>
+     * If there is one or more idle instance available in the pool, then an
+     * idle instance will be selected based on the value of {@link #getLifo()},
+     * activated and returned. If activation fails, or {@link #getTestOnBorrow()
+     * testOnBorrow} is set to <code>true</code> and validation fails, the
+     * instance is destroyed and the next available instance is examined. This
+     * continues until either a valid instance is returned or there are no more
+     * idle instances available.
+     * </p>
+     * <p>
+     * If there are no idle instances available in the pool, behavior depends on
+     * the {@link #getMaxTotal() maxTotal}, (if applicable)
+     * {@link #getBlockWhenExhausted()} and the value passed in to the
+     * <code>borrowMaxWaitMillis</code> parameter. If the number of instances
+     * checked out from the pool is less than <code>maxTotal,</code> a new
+     * instance is created, activated and (if applicable) validated and returned
+     * to the caller. If validation fails, a <code>NoSuchElementException</code>
+     * is thrown.
+     * </p>
+     * <p>
+     * If the pool is exhausted (no available idle instances and no capacity to
+     * create new ones), this method will either block (if
+     * {@link #getBlockWhenExhausted()} is true) or throw a
+     * <code>NoSuchElementException</code> (if
+     * {@link #getBlockWhenExhausted()} is false). The length of time that this
+     * method will block when {@link #getBlockWhenExhausted()} is true is
+     * determined by the value passed in to the <code>borrowMaxWaitMillis</code>
+     * parameter.
+     * </p>
+     * <p>
+     * When the pool is exhausted, multiple calling threads may be
+     * simultaneously blocked waiting for instances to become available. A
+     * "fairness" algorithm has been implemented to ensure that threads receive
+     * available instances in request arrival order.
+     * </p>
+     *
+     * @param borrowMaxWaitMillis The time to wait in milliseconds for an object
+     *                            to become available
+     *
+     * @return object instance from the pool
+     *
+     * @throws NoSuchElementException if an instance cannot be returned
+     *
+     * @throws Exception if an object instance cannot be returned due to an
+     *                   error
+     */
+    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
+        assertOpen();
+
+        final AbandonedConfig ac = this.abandonedConfig;
+        if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
+                (getNumIdle() < 2) &&
+                (getNumActive() > getMaxTotal() - 3) ) {
+            removeAbandoned(ac);
+        }
+
+        PooledObject<T> p = null;
+
+        // Get local copy of current config so it is consistent for entire
+        // method execution
+        final boolean blockWhenExhausted = getBlockWhenExhausted();
+
+        boolean create;
+        final long waitTime = System.currentTimeMillis();
+
+        while (p == null) {
+            create = false;
+            p = idleObjects.pollFirst();
+            if (p == null) {
+                p = create();
+                if (p != null) {
+                    create = true;
+                }
+            }
+            if (blockWhenExhausted) {
+                if (p == null) {
+                    if (borrowMaxWaitMillis < 0) {
+                        p = idleObjects.takeFirst();
+                    } else {
+                        p = idleObjects.pollFirst(borrowMaxWaitMillis,
+                                TimeUnit.MILLISECONDS);
+                    }
+                }
+                if (p == null) {
+                    throw new NoSuchElementException(
+                            "Timeout waiting for idle object");
+                }
+            } else {
+                if (p == null) {
+                    throw new NoSuchElementException("Pool exhausted");
+                }
+            }
+            if (!p.allocate()) {
+                p = null;
+            }
+
+            if (p != null) {
+                try {
+                    factory.activateObject(p);
+                } catch (final Exception e) {
+                    try {
+                        destroy(p);
+                    } catch (final Exception e1) {
+                        // Ignore - activation failure is more important
+                    }
+                    p = null;
+                    if (create) {
+                        final NoSuchElementException nsee = new NoSuchElementException(
+                                "Unable to activate object");
+                        nsee.initCause(e);
+                        throw nsee;
+                    }
+                }
+                if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
+                    boolean validate = false;
+                    Throwable validationThrowable = null;
+                    try {
+                        validate = factory.validateObject(p);
+                    } catch (final Throwable t) {
+                        PoolUtils.checkRethrow(t);
+                        validationThrowable = t;
+                    }
+                    if (!validate) {
+                        try {
+                            destroy(p);
+                            destroyedByBorrowValidationCount.incrementAndGet();
+                        } catch (final Exception e) {
+                            // Ignore - validation failure is more important
+                        }
+                        p = null;
+                        if (create) {
+                            final NoSuchElementException nsee = new NoSuchElementException(
+                                    "Unable to validate object");
+                            nsee.initCause(validationThrowable);
+                            throw nsee;
+                        }
+                    }
+                }
+            }
+        }
+
+        updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
+
+        return p.getObject();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * If {@link #getMaxIdle() maxIdle} is set to a positive value and the
+     * number of idle instances has reached this value, the returning instance
+     * is destroyed.
+     * </p>
+     * <p>
+     * If {@link #getTestOnReturn() testOnReturn} == true, the returning
+     * instance is validated before being returned to the idle instance pool. In
+     * this case, if validation fails, the instance is destroyed.
+     * </p>
+     * <p>
+     * Exceptions encountered destroying objects for any reason are swallowed
+     * but notified via a {@link SwallowedExceptionListener}.
+     * </p>
+     */
+    @Override
+    public void returnObject(final T obj) {
+        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
+
+        if (p == null) {
+            if (!isAbandonedConfig()) {
+                throw new IllegalStateException(
+                        "Returned object not currently part of this pool");
+            }
+            return; // Object was abandoned and removed
+        }
+
+        markReturningState(p);
+
+        final long activeTime = p.getActiveTimeMillis();
+
+        if (getTestOnReturn() && !factory.validateObject(p)) {
+            try {
+                destroy(p);
+            } catch (final Exception e) {
+                swallowException(e);
+            }
+            try {
+                ensureIdle(1, false);
+            } catch (final Exception e) {
+                swallowException(e);
+            }
+            updateStatsReturn(activeTime);
+            return;
+        }
+
+        try {
+            factory.passivateObject(p);
+        } catch (final Exception e1) {
+            swallowException(e1);
+            try {
+                destroy(p);
+            } catch (final Exception e) {
+                swallowException(e);
+            }
+            try {
+                ensureIdle(1, false);
+            } catch (final Exception e) {
+                swallowException(e);
+            }
+            updateStatsReturn(activeTime);
+            return;
+        }
+
+        if (!p.deallocate()) {
+            throw new IllegalStateException(
+                    "Object has already been returned to this pool or is invalid");
+        }
+
+        final int maxIdleSave = getMaxIdle();
+        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
+            try {
+                destroy(p);
+            } catch (final Exception e) {
+                swallowException(e);
+            }
+        } else {
+            if (getLifo()) {
+                idleObjects.addFirst(p);
+            } else {
+                idleObjects.addLast(p);
+            }
+            if (isClosed()) {
+                // Pool closed while object was being added to idle objects.
+                // Make sure the returned object is destroyed rather than left
+                // in the idle object pool (which would effectively be a leak)
+                clear();
+            }
+        }
+        updateStatsReturn(activeTime);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Activation of this method decrements the active count and attempts to
+     * destroy the instance.
+     * </p>
+     *
+     * @throws Exception             if an exception occurs destroying the
+     *                               object
+     * @throws IllegalStateException if obj does not belong to this pool
+     */
+    @Override
+    public void invalidateObject(final T obj) throws Exception {
+        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
+        if (p == null) {
+            if (isAbandonedConfig()) {
+                return;
+            }
+            throw new IllegalStateException(
+                    "Invalidated object not currently part of this pool");
+        }
+        synchronized (p) {
+            if (p.getState() != PooledObjectState.INVALID) {
+                destroy(p);
+            }
+        }
+        ensureIdle(1, false);
+    }
+
+    /**
+     * Clears any objects sitting idle in the pool by removing them from the
+     * idle instance pool and then invoking the configured
+     * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each
+     * idle instance.
+     * <p>
+     * Implementation notes:
+     * </p>
+     * <ul>
+     * <li>This method does not destroy or effect in any way instances that are
+     * checked out of the pool when it is invoked.</li>
+     * <li>Invoking this method does not prevent objects being returned to the
+     * idle instance pool, even during its execution. Additional instances may
+     * be returned while removed items are being destroyed.</li>
+     * <li>Exceptions encountered destroying idle instances are swallowed
+     * but notified via a {@link SwallowedExceptionListener}.</li>
+     * </ul>
+     */
+    @Override
+    public void clear() {
+        PooledObject<T> p = idleObjects.poll();
+
+        while (p != null) {
+            try {
+                destroy(p);
+            } catch (final Exception e) {
+                swallowException(e);
+            }
+            p = idleObjects.poll();
+        }
+    }
+
+    @Override
+    public int getNumActive() {
+        return allObjects.size() - idleObjects.size();
+    }
+
+    @Override
+    public int getNumIdle() {
+        return idleObjects.size();
+    }
+
+    /**
+     * Closes the pool. Once the pool is closed, {@link #borrowObject()} will
+     * fail with IllegalStateException, but {@link #returnObject(Object)} and
+     * {@link #invalidateObject(Object)} will continue to work, with returned
+     * objects destroyed on return.
+     * <p>
+     * Destroys idle instances in the pool by invoking {@link #clear()}.
+     * </p>
+     */
+    @Override
+    public void close() {
+        if (isClosed()) {
+            return;
+        }
+
+        synchronized (closeLock) {
+            if (isClosed()) {
+                return;
+            }
+
+            // Stop the evictor before the pool is closed since evict() calls
+            // assertOpen()
+            stopEvitor();
+
+            closed = true;
+            // This clear removes any idle objects
+            clear();
+
+            jmxUnregister();
+
+            // Release any threads that were waiting for an object
+            idleObjects.interuptTakeWaiters();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Successive activations of this method examine objects in sequence,
+     * cycling through objects in oldest-to-youngest order.
+     * </p>
+     */
+    @Override
+    public void evict() throws Exception {
+        assertOpen();
+
+        if (idleObjects.size() > 0) {
+
+            PooledObject<T> underTest = null;
+            final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
+
+            synchronized (evictionLock) {
+                final EvictionConfig evictionConfig = new EvictionConfig(
+                        getMinEvictableIdleTimeMillis(),
+                        getSoftMinEvictableIdleTimeMillis(),
+                        getMinIdle());
+
+                final boolean testWhileIdle = getTestWhileIdle();
+
+                for (int i = 0, m = getNumTests(); i < m; i++) {
+                    if (evictionIterator == null || !evictionIterator.hasNext()) {
+                        evictionIterator = new EvictionIterator(idleObjects);
+                    }
+                    if (!evictionIterator.hasNext()) {
+                        // Pool exhausted, nothing to do here
+                        return;
+                    }
+
+                    try {
+                        underTest = evictionIterator.next();
+                    } catch (final NoSuchElementException nsee) {
+                        // Object was borrowed in another thread
+                        // Don't count this as an eviction test so reduce i;
+                        i--;
+                        evictionIterator = null;
+                        continue;
+                    }
+
+                    if (!underTest.startEvictionTest()) {
+                        // Object was borrowed in another thread
+                        // Don't count this as an eviction test so reduce i;
+                        i--;
+                        continue;
+                    }
+
+                    // User provided eviction policy could throw all sorts of
+                    // crazy exceptions. Protect against such an exception
+                    // killing the eviction thread.
+                    boolean evict;
+                    try {
+                        evict = evictionPolicy.evict(evictionConfig, underTest,
+                                idleObjects.size());
+                    } catch (final Throwable t) {
+                        // Slightly convoluted as SwallowedExceptionListener
+                        // uses Exception rather than Throwable
+                        PoolUtils.checkRethrow(t);
+                        swallowException(new Exception(t));
+                        // Don't evict on error conditions
+                        evict = false;
+                    }
+
+                    if (evict) {
+                        destroy(underTest);
+                        destroyedByEvictorCount.incrementAndGet();
+                    } else {
+                        if (testWhileIdle) {
+                            boolean active = false;
+                            try {
+                                factory.activateObject(underTest);
+                                active = true;
+                            } catch (final Exception e) {
+                                destroy(underTest);
+                                destroyedByEvictorCount.incrementAndGet();
+                            }
+                            if (active) {
+                                if (!factory.validateObject(underTest)) {
+                                    destroy(underTest);
+                                    destroyedByEvictorCount.incrementAndGet();
+                                } else {
+                                    try {
+                                        factory.passivateObject(underTest);
+                                    } catch (final Exception e) {
+                                        destroy(underTest);
+                                        destroyedByEvictorCount.incrementAndGet();
+                                    }
+                                }
+                            }
+                        }
+                        if (!underTest.endEvictionTest(idleObjects)) {
+                            // TODO - May need to add code here once additional
+                            // states are used
+                        }
+                    }
+                }
+            }
+        }
+        final AbandonedConfig ac = this.abandonedConfig;
+        if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
+            removeAbandoned(ac);
+        }
+    }
+
+    /**
+     * Tries to ensure that {@link #getMinIdle()} idle instances are available
+     * in the pool.
+     *
+     * @throws Exception If the associated factory throws an exception
+     * @since 2.4
+     */
+    public void preparePool() throws Exception {
+        if (getMinIdle() < 1) {
+            return;
+        }
+        ensureMinIdle();
+    }
+
+    /**
+     * Attempts to create a new wrapped pooled object.
+     * <p>
+     * If there are {@link #getMaxTotal()} objects already in circulation
+     * or in process of being created, this method returns null.
+     * </p>
+     *
+     * @return The new wrapped pooled object
+     *
+     * @throws Exception if the object factory's {@code makeObject} fails
+     */
+    private PooledObject<T> create() throws Exception {
+        int localMaxTotal = getMaxTotal();
+        // This simplifies the code later in this method
+        if (localMaxTotal < 0) {
+            localMaxTotal = Integer.MAX_VALUE;
+        }
+
+        final long localStartTimeMillis = System.currentTimeMillis();
+        final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0);
+
+        // Flag that indicates if create should:
+        // - TRUE:  call the factory to create an object
+        // - FALSE: return null
+        // - null:  loop and re-test the condition that determines whether to
+        //          call the factory
+        Boolean create = null;
+        while (create == null) {
+            synchronized (makeObjectCountLock) {
+                final long newCreateCount = createCount.incrementAndGet();
+                if (newCreateCount > localMaxTotal) {
+                    // The pool is currently at capacity or in the process of
+                    // making enough new objects to take it to capacity.
+                    createCount.decrementAndGet();
+                    if (makeObjectCount == 0) {
+                        // There are no makeObject() calls in progress so the
+                        // pool is at capacity. Do not attempt to create a new
+                        // object. Return and wait for an object to be returned
+                        create = Boolean.FALSE;
+                    } else {
+                        // There are makeObject() calls in progress that might
+                        // bring the pool to capacity. Those calls might also
+                        // fail so wait until they complete and then re-test if
+                        // the pool is at capacity or not.
+                        makeObjectCountLock.wait(localMaxWaitTimeMillis);
+                    }
+                } else {
+                    // The pool is not at capacity. Create a new object.
+                    makeObjectCount++;
+                    create = Boolean.TRUE;
+                }
+            }
+
+            // Do not block more if maxWaitTimeMillis is set.
+            if (create == null &&
+                (localMaxWaitTimeMillis > 0 &&
+                 System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) {
+                create = Boolean.FALSE;
+            }
+        }
+
+        if (!create.booleanValue()) {
+            return null;
+        }
+
+        final PooledObject<T> p;
+        try {
+            p = factory.makeObject();
+        } catch (final Throwable e) {
+            createCount.decrementAndGet();
+            throw e;
+        } finally {
+            synchronized (makeObjectCountLock) {
+                makeObjectCount--;
+                makeObjectCountLock.notifyAll();
+            }
+        }
+
+        final AbandonedConfig ac = this.abandonedConfig;
+        if (ac != null && ac.getLogAbandoned()) {
+            p.setLogAbandoned(true);
+            // TODO: in 3.0, this can use the method defined on PooledObject
+            if (p instanceof DefaultPooledObject<?>) {
+                ((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace());
+            }
+        }
+
+        createdCount.incrementAndGet();
+        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
+        return p;
+    }
+
+    /**
+     * Destroys a wrapped pooled object.
+     *
+     * @param toDestroy The wrapped pooled object to destroy
+     *
+     * @throws Exception If the factory fails to destroy the pooled object
+     *                   cleanly
+     */
+    private void destroy(final PooledObject<T> toDestroy) throws Exception {
+        toDestroy.invalidate();
+        idleObjects.remove(toDestroy);
+        allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
+        try {
+            factory.destroyObject(toDestroy);
+        } finally {
+            destroyedCount.incrementAndGet();
+            createCount.decrementAndGet();
+        }
+
+        if (idleObjects.isEmpty() && idleObjects.hasTakeWaiters()) {
+            // POOL-356.
+            // In case there are already threads waiting on something in the pool
+            // (e.g. idleObjects.takeFirst(); then we need to provide them a fresh instance.
+            // Otherwise they will be stuck forever (or until timeout)
+            final PooledObject<T> freshPooled = create();
+            idleObjects.put(freshPooled);
+        }
+    }
+
+    @Override
+    void ensureMinIdle() throws Exception {
+        ensureIdle(getMinIdle(), true);
+    }
+
+    /**
+     * Tries to ensure that {@code idleCount} idle instances exist in the pool.
+     * <p>
+     * Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount}
+     * or the total number of objects (idle, checked out, or being created) reaches
+     * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless
+     * there are threads waiting to check out instances from the pool.
+     * </p>
+     *
+     * @param idleCount the number of idle instances desired
+     * @param always true means create instances even if the pool has no threads waiting
+     * @throws Exception if the factory's makeObject throws
+     */
+    private void ensureIdle(final int idleCount, final boolean always) throws Exception {
+        if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
+            return;
+        }
+
+        while (idleObjects.size() < idleCount) {
+            final PooledObject<T> p = create();
+            if (p == null) {
+                // Can't create objects, no reason to think another call to
+                // create will work. Give up.
+                break;
+            }
+            if (getLifo()) {
+                idleObjects.addFirst(p);
+            } else {
+                idleObjects.addLast(p);
+            }
+        }
+        if (isClosed()) {
+            // Pool closed while object was being added to idle objects.
+            // Make sure the returned object is destroyed rather than left
+            // in the idle object pool (which would effectively be a leak)
+            clear();
+        }
+    }
+
+    /**
+     * Creates an object, and place it into the pool. addObject() is useful for
+     * "pre-loading" a pool with idle objects.
+     * <p>
+     * If there is no capacity available to add to the pool, this is a no-op
+     * (no exception, no impact to the pool). </p>
+     */
+    @Override
+    public void addObject() throws Exception {
+        assertOpen();
+        if (factory == null) {
+            throw new IllegalStateException(
+                    "Cannot add objects without a factory.");
+        }
+        final PooledObject<T> p = create();
+        addIdleObject(p);
+    }
+
+    /**
+     * Adds the provided wrapped pooled object to the set of idle objects for
+     * this pool. The object must already be part of the pool.  If {@code p}
+     * is null, this is a no-op (no exception, but no impact on the pool).
+     *
+     * @param p The object to make idle
+     *
+     * @throws Exception If the factory fails to passivate the object
+     */
+    private void addIdleObject(final PooledObject<T> p) throws Exception {
+        if (p != null) {
+            factory.passivateObject(p);
+            if (getLifo()) {
+                idleObjects.addFirst(p);
+            } else {
+                idleObjects.addLast(p);
+            }
+        }
+    }
+
+    /**
+     * Calculates the number of objects to test in a run of the idle object
+     * evictor.
+     *
+     * @return The number of objects to test for validity
+     */
+    private int getNumTests() {
+        final int numTestsPerEvictionRun = getNumTestsPerEvictionRun();
+        if (numTestsPerEvictionRun >= 0) {
+            return Math.min(numTestsPerEvictionRun, idleObjects.size());
+        }
+        return (int) (Math.ceil(idleObjects.size() /
+                Math.abs((double) numTestsPerEvictionRun)));
+    }
+
+    /**
+     * Recovers abandoned objects which have been checked out but
+     * not used since longer than the removeAbandonedTimeout.
+     *
+     * @param ac The configuration to use to identify abandoned objects
+     */
+    private void removeAbandoned(final AbandonedConfig ac) {
+        // Generate a list of abandoned objects to remove
+        final long now = System.currentTimeMillis();
+        final long timeout =
+                now - (ac.getRemoveAbandonedTimeout() * 1000L);
+        final ArrayList<PooledObject<T>> remove = new ArrayList<>();
+        final Iterator<PooledObject<T>> it = allObjects.values().iterator();
+        while (it.hasNext()) {
+            final PooledObject<T> pooledObject = it.next();
+            synchronized (pooledObject) {
+                if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
+                        pooledObject.getLastUsedTime() <= timeout) {
+                    pooledObject.markAbandoned();
+                    remove.add(pooledObject);
+                }
+            }
+        }
+
+        // Now remove the abandoned objects
+        final Iterator<PooledObject<T>> itr = remove.iterator();
+        while (itr.hasNext()) {
+            final PooledObject<T> pooledObject = itr.next();
+            if (ac.getLogAbandoned()) {
+                pooledObject.printStackTrace(ac.getLogWriter());
+            }
+            try {
+                invalidateObject(pooledObject.getObject());
+            } catch (final Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+
+    //--- Usage tracking support -----------------------------------------------
+
+    @Override
+    public void use(final T pooledObject) {
+        final AbandonedConfig ac = this.abandonedConfig;
+        if (ac != null && ac.getUseUsageTracking()) {
+            final PooledObject<T> wrapper = allObjects.get(new IdentityWrapper<>(pooledObject));
+            wrapper.use();
+        }
+    }
+
+
+    //--- JMX support ----------------------------------------------------------
+
+    private volatile String factoryType = null;
+
+    /**
+     * Returns an estimate of the number of threads currently blocked waiting for
+     * an object from the pool. This is intended for monitoring only, not for
+     * synchronization control.
+     *
+     * @return The estimate of the number of threads currently blocked waiting
+     *         for an object from the pool
+     */
+    @Override
+    public int getNumWaiters() {
+        if (getBlockWhenExhausted()) {
+            return idleObjects.getTakeQueueLength();
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the type - including the specific type rather than the generic -
+     * of the factory.
+     *
+     * @return A string representation of the factory type
+     */
+    @Override
+    public String getFactoryType() {
+        // Not thread safe. Accept that there may be multiple evaluations.
+        if (factoryType == null) {
+            final StringBuilder result = new StringBuilder();
+            result.append(factory.getClass().getName());
+            result.append('<');
+            final Class<?> pooledObjectType =
+                    PoolImplUtils.getFactoryType(factory.getClass());
+            result.append(pooledObjectType.getName());
+            result.append('>');
+            factoryType = result.toString();
+        }
+        return factoryType;
+    }
+
+    /**
+     * Provides information on all the objects in the pool, both idle (waiting
+     * to be borrowed) and active (currently borrowed).
+     * <p>
+     * Note: This is named listAllObjects so it is presented as an operation via
+     * JMX. That means it won't be invoked unless the explicitly requested
+     * whereas all attributes will be automatically requested when viewing the
+     * attributes for an object in a tool like JConsole.
+     * </p>
+     *
+     * @return Information grouped on all the objects in the pool
+     */
+    @Override
+    public Set<DefaultPooledObjectInfo> listAllObjects() {
+        final Set<DefaultPooledObjectInfo> result =
+                new HashSet<>(allObjects.size());
+        for (final PooledObject<T> p : allObjects.values()) {
+            result.add(new DefaultPooledObjectInfo(p));
+        }
+        return result;
+    }
+
+    // --- configuration attributes --------------------------------------------
+
+    private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
+    private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
+    private final PooledObjectFactory<T> factory;
+
+
+    // --- internal attributes -------------------------------------------------
+
+    /*
+     * All of the objects currently associated with this pool in any state. It
+     * excludes objects that have been destroyed. The size of
+     * {@link #allObjects} will always be less than or equal to {@link
+     * #_maxActive}. Map keys are pooled objects, values are the PooledObject
+     * wrappers used internally by the pool.
+     */
+    private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
+        new ConcurrentHashMap<>();
+    /*
+     * The combined count of the currently created objects and those in the
+     * process of being created. Under load, it may exceed {@link #_maxActive}
+     * if multiple threads try and create a new object at the same time but
+     * {@link #create()} will ensure that there are never more than
+     * {@link #_maxActive} objects created at any one time.
+     */
+    private final AtomicLong createCount = new AtomicLong(0);
+    private long makeObjectCount = 0;
+    private final Object makeObjectCountLock = new Object();
+    private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
+
+    // JMX specific attributes
+    private static final String ONAME_BASE =
+        "org.apache.commons.pool2:type=GenericObjectPool,name=";
+
+    // Additional configuration properties for abandoned object tracking
+    private volatile AbandonedConfig abandonedConfig = null;
+
+    @Override
+    protected void toStringAppendFields(final StringBuilder builder) {
+        super.toStringAppendFields(builder);
+        builder.append(", factoryType=");
+        builder.append(factoryType);
+        builder.append(", maxIdle=");
+        builder.append(maxIdle);
+        builder.append(", minIdle=");
+        builder.append(minIdle);
+        builder.append(", factory=");
+        builder.append(factory);
+        builder.append(", allObjects=");
+        builder.append(allObjects);
+        builder.append(", createCount=");
+        builder.append(createCount);
+        builder.append(", idleObjects=");
+        builder.append(idleObjects);
+        builder.append(", abandonedConfig=");
+        builder.append(abandonedConfig);
+    }
+
+}