    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.pool2.impl;
    018
    019import java.time.Duration;
    020import java.time.Instant;
    021import java.util.ArrayList;
    022import java.util.Deque;
    023import java.util.HashMap;
    024import java.util.Iterator;
    025import java.util.List;
    026import java.util.Map;
    027import java.util.Map.Entry;
    028import java.util.NoSuchElementException;
    029import java.util.Objects;
    030import java.util.TreeMap;
    031import java.util.concurrent.BlockingDeque;
    032import java.util.concurrent.ConcurrentHashMap;
    033import java.util.concurrent.atomic.AtomicBoolean;
    034import java.util.concurrent.atomic.AtomicInteger;
    035import java.util.concurrent.atomic.AtomicLong;
    036import java.util.concurrent.locks.Lock;
    037import java.util.concurrent.locks.ReadWriteLock;
    038import java.util.concurrent.locks.ReentrantReadWriteLock;
    039import java.util.stream.Collectors;
    040
    041import org.apache.commons.pool2.DestroyMode;
    042import org.apache.commons.pool2.KeyedObjectPool;
    043import org.apache.commons.pool2.KeyedPooledObjectFactory;
    044import org.apache.commons.pool2.PoolUtils;
    045import org.apache.commons.pool2.PooledObject;
    046import org.apache.commons.pool2.PooledObjectState;
    047import org.apache.commons.pool2.SwallowedExceptionListener;
    048import org.apache.commons.pool2.UsageTracking;
    049
    050/**
    051 * A configurable {@code KeyedObjectPool} implementation.
    052 * <p>
    053 * When coupled with the appropriate {@link KeyedPooledObjectFactory},
    054 * {@code GenericKeyedObjectPool} provides robust pooling functionality for
    055 * keyed objects. A {@code GenericKeyedObjectPool} can be viewed as a map
    056 * of sub-pools, keyed on the (unique) key values provided to the
    057 * {@link #preparePool preparePool}, {@link #addObject addObject} or
    058 * {@link #borrowObject borrowObject} methods. Each time a new key value is
    059 * provided to one of these methods, a sub-new pool is created under the given
    060 * key to be managed by the containing {@code GenericKeyedObjectPool.}
    061 * </p>
    062 * <p>
    063 * Note that the current implementation uses a ConcurrentHashMap which uses
    064 * equals() to compare keys.
    065 * This means that distinct instance keys must be distinguishable using equals.
    066 * </p>
    067 * <p>
    068 * Optionally, one may configure the pool to examine and possibly evict objects
    069 * as they sit idle in the pool and to ensure that a minimum number of idle
    070 * objects is maintained for each key. This is performed by an "idle object
    071 * eviction" thread, which runs asynchronously. Caution should be used when
    072 * configuring this optional feature. Eviction runs contend with client threads
    073 * for access to objects in the pool, so if they run too frequently performance
    074 * issues may result.
    075 * </p>
    076 * <p>
    077 * Implementation note: To prevent possible deadlocks, care has been taken to
    078 * ensure that no call to a factory method will occur within a synchronization
    079 * block. See POOL-125 and DBCP-44 for more information.
    080 * </p>
    081 * <p>
    082 * This class is intended to be thread-safe.
    083 * </p>
    084 *
    085 * @see GenericObjectPool
    086 * @param <K> The type of keys maintained by this pool.
    087 * @param <T> Type of element pooled in this pool.
    088 * @since 2.0
    089 */
    090public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T>
    091        implements KeyedObjectPool<K, T>, GenericKeyedObjectPoolMXBean<K>, UsageTracking<T> {
    092
    093    /**
    094     * Maintains information on the per key queue for a given key.
    095     *
    096     * @param <S> type of objects in the pool
    097     */
    098    private static final class ObjectDeque<S> {
    099
    100        private final LinkedBlockingDeque<PooledObject<S>> idleObjects;
    101
    102        /*
    103         * Number of instances created - number destroyed.
    104         * Invariant: createCount <= maxTotalPerKey
    105         */
    106        private final AtomicInteger createCount = new AtomicInteger();
    107
    108        private long makeObjectCount;
    109        private final Object makeObjectCountLock = new Object();
    110
    111        /*
    112         * The map is keyed on pooled instances, wrapped to ensure that
    113         * they work properly as keys.
    114         */
    115        private final Map<IdentityWrapper<S>, PooledObject<S>> allObjects =
    116                new ConcurrentHashMap<>();
    117
    118        /*
    119         * Number of threads with registered interest in this key.
    120         * register(K) increments this counter and deRegister(K) decrements it.
    121         * Invariant: empty keyed pool will not be dropped unless numInterested
    122         *            is 0.
    123         */
    124        private final AtomicLong numInterested = new AtomicLong();
    125
    126        /**
    127         * Constructs a new ObjectDeque with the given fairness policy.
    128         * @param fairness true means client threads waiting to borrow / return instances
    129         * will be served as if waiting in a FIFO queue.
    130         */
    131        private ObjectDeque(final boolean fairness) {
    132            idleObjects = new LinkedBlockingDeque<>(fairness);
    133        }
    134
    135        /**
    136         * Gets all the objects for the current key.
    137         *
    138         * @return All the objects.
    139         */
    140        Map<IdentityWrapper<S>, PooledObject<S>> getAllObjects() {
    141            return allObjects;
    142        }
    143
    144        /**
    145         * Gets the number of instances created - number destroyed.
    146         * Should always be less than or equal to maxTotalPerKey.
    147         *
    148         * @return The net instance addition count for this deque.
    149         */
    150        AtomicInteger getCreateCount() {
    151            return createCount;
    152        }
    153
    154        /**
    155         * Gets the idle objects for the current key.
    156         *
    157         * @return The idle objects.
    158         */
    159        LinkedBlockingDeque<PooledObject<S>> getIdleObjects() {
    160            return idleObjects;
    161        }
    162
    163        /**
    164         * Gets the number of threads with an interest registered in this key.
    165         *
    166         * @return The number of threads with a registered interest in this key.
    167         */
    168        AtomicLong getNumInterested() {
    169            return numInterested;
    170        }
    171
    172        @Override
    173        public String toString() {
    174            final StringBuilder builder = new StringBuilder();
    175            builder.append("ObjectDeque [idleObjects=");
    176            builder.append(idleObjects);
    177            builder.append(", createCount=");
    178            builder.append(createCount);
    179            builder.append(", allObjects=");
    180            builder.append(allObjects);
    181            builder.append(", numInterested=");
    182            builder.append(numInterested);
    183            builder.append("]");
    184            return builder.toString();
    185        }
    186
    187    }
    188
    189    private static final Integer ZERO = Integer.valueOf(0);
    190
    191    // JMX specific attributes
    192    private static final String ONAME_BASE =
    193            "org.apache.commons.pool2:type=GenericKeyedObjectPool,name=";
    194
    195    private volatile int maxIdlePerKey =
    196            GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY;
    197
    198    private volatile int minIdlePerKey =
    199            GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY;
    200
    201    private volatile int maxTotalPerKey =
    202            GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;
    203
    204    private volatile boolean reuseCapacityOnReturn =
    205            GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_RETURN;
    206
    207    private volatile boolean reuseCapacityOnMaintenance =
    208            GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE;
    209
    210    private final KeyedPooledObjectFactory<K, T> factory;
    211
    212    private final boolean fairness;
    213
    214    /*
    215     * My hash of sub-pools (ObjectQueue). The list of keys <strong>must</strong> be kept
    216     * in step with {@link #poolKeyList} using {@link #keyLock} to ensure any
    217     * changes to the list of current keys is made in a thread-safe manner.
    218     *
    219     * Correct operation of the pool requires that a Map implementation is used that
    220     * supports concurrent read and write (e.g. ensureMinIdle() iterates over the key set
    221     * while other threads may be adding or removing keys) therefore explicitly define
    222     * this field as ConcurrentHashMap rather than Map.
    223     */
    224    private final ConcurrentHashMap<K, ObjectDeque<T>> poolMap =
    225            new ConcurrentHashMap<>(); // @GuardedBy("keyLock") for write access (and some read access)
    226
    227    /*
    228     * List of pool keys - used to control eviction order. The list of keys
    229     * <strong>must</strong> be kept in step with {@link #poolMap} using {@link #keyLock}
    230     * to ensure any changes to the list of current keys is made in a
    231     * thread-safe manner.
    232     */
    233    private final ArrayList<K> poolKeyList = new ArrayList<>(); // @GuardedBy("keyLock")
    234
    235    private final ReadWriteLock keyLock = new ReentrantReadWriteLock(true);
    236
    237    /*
    238     * The combined count of the currently active objects for all keys and those
    239     * in the process of being created. Under load, it may exceed
    240     * {@link #maxTotal} but there will never be more than {@link #maxTotal}
    241     * created at any one time.
    242     */
    243    private final AtomicInteger numTotal = new AtomicInteger();
    244
    245    private Iterator<K> evictionKeyIterator; // @GuardedBy("evictionLock")
    246
    247    private K evictionKey; // @GuardedBy("evictionLock")
    248
    249    /**
    250     * Constructs a new {@code GenericKeyedObjectPool} using defaults from
    251     * {@link GenericKeyedObjectPoolConfig}.
    252     * @param factory the factory to be used to create entries
    253     */
    254    public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K, T> factory) {
    255        this(factory, new GenericKeyedObjectPoolConfig<>());
    256    }
    257
    258    /**
    259     * Constructs a new {@code GenericKeyedObjectPool} using a specific
    260     * configuration.
    261     *
    262     * @param factory the factory to be used to create entries
    263     * @param config    The configuration to use for this pool instance. The
    264     *                  configuration is used by value. Subsequent changes to
    265     *                  the configuration object will not be reflected in the
    266     *                  pool.
    267     */
    268    public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K, T> factory,
    269            final GenericKeyedObjectPoolConfig<T> config) {
    270
    271        super(config, ONAME_BASE, config.getJmxNamePrefix());
    272
    273        if (factory == null) {
    274            jmxUnregister(); // tidy up
    275            throw new IllegalArgumentException("Factory may not be null");
    276        }
    277        this.factory = factory;
    278        this.fairness = config.getFairness();
    279
    280        setConfig(config);
    281    }
    282
    283    /**
    284     * Creates a new {@code GenericKeyedObjectPool} that tracks and destroys
    285     * objects that are checked out, but never returned to the pool.
    286     *
    287     * @param factory   The object factory to be used to create object instances
    288     *                  used by this pool
    289     * @param config    The base pool configuration to use for this pool instance.
    290     *                  The configuration is used by value. Subsequent changes to
    291     *                  the configuration object will not be reflected in the
    292     *                  pool.
    293     * @param abandonedConfig  Configuration for abandoned object identification
    294     *                         and removal.  The configuration is used by value.
    295     * @since 2.10.0
    296     */
    297    public GenericKeyedObjectPool(final KeyedPooledObjectFactory<K, T> factory,
    298            final GenericKeyedObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
    299        this(factory, config);
    300        setAbandonedConfig(abandonedConfig);
    301    }
    302
    303    /**
    304     * Add an object to the set of idle objects for a given key.
    305     * If the object is null this is a no-op.
    306     *
    307     * @param key The key to associate with the idle object
    308     * @param p The wrapped object to add.
    309     * @throws Exception If the associated factory fails to passivate the object
    310     */
    311    private void addIdleObject(final K key, final PooledObject<T> p) throws Exception {
    312        if (PooledObject.nonNull(p)) {
    313            factory.passivateObject(key, p);
    314            final BlockingDeque<PooledObject<T>> idleObjects = poolMap.get(key).getIdleObjects();
    315            if (getLifo()) {
    316                idleObjects.addFirst(p);
    317            } else {
    318                idleObjects.addLast(p);
    319            }
    320        }
    321    }
    322
    323    /**
    324     * Create an object using the {@link KeyedPooledObjectFactory#makeObject
    325     * factory}, passivate it, and then place it in the idle object pool.
    326     * {@code addObject} is useful for "pre-loading" a pool with idle
    327     * objects.
    328     * <p>
    329     * If there is no capacity available to add to the pool under the given key,
    330     * this is a no-op (no exception, no impact to the pool).
    331     * </p>
    332     * <p>
    333     * If the factory returns null when creating an instance,
    334     * a {@code NullPointerException} is thrown.
    335     * </p>
    336     *
    337     * @param key the key a new instance should be added to
    338     * @throws Exception when {@link KeyedPooledObjectFactory#makeObject}
    339     *                   fails.
    340     */
    341    @Override
    342    public void addObject(final K key) throws Exception {
    343        assertOpen();
    344        final ObjectDeque<T> objectDeque = register(key);
    345        try {
    346            // Attempt create and add only if there is capacity to add
    347            // > to the overall instance count
    348            // > to the pool under the key
    349            final int maxtTotalPerKey = getMaxTotalPerKey();
    350            final int maxTotal = getMaxTotal();
    351            if ((maxTotal < 0 || getNumActive() + getNumIdle() < maxTotal)
    352                    && (maxtTotalPerKey < 0 || objectDeque.allObjects.size() < maxtTotalPerKey)) {
    353                // Attempt to create and add a new instance under key
    354                addIdleObject(key, create(key, getMaxWaitDuration()));
    355            }
    356        } finally {
    357            deregister(key);
    358        }
    359    }
    360
    361    /**
    362     * Equivalent to <code>{@link #borrowObject(Object, long) borrowObject}(key,
    363     * {@link #getMaxWaitDuration()})</code>.
    364     *
    365     * {@inheritDoc}
    366     */
    367    @Override
    368    public T borrowObject(final K key) throws Exception {
    369        return borrowObject(key, getMaxWaitDuration().toMillis());
    370    }
    371
    372    /**
    373     * Borrows an object from the sub-pool associated with the given key using
    374     * the specified waiting time which only applies if
    375     * {@link #getBlockWhenExhausted()} is true.
    376     * <p>
    377     * If there is one or more idle instances available in the sub-pool
    378     * associated with the given key, then an idle instance will be selected
    379     * based on the value of {@link #getLifo()}, activated and returned.  If
    380     * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to
    381     * {@code true} and validation fails, the instance is destroyed and the
    382     * next available instance is examined.  This continues until either a valid
    383     * instance is returned or there are no more idle instances available.
    384     * </p>
    385     * <p>
    386     * If there are no idle instances available in the sub-pool associated with
    387     * the given key, behavior depends on the {@link #getMaxTotalPerKey()
    388     * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable)
    389     * {@link #getBlockWhenExhausted()} and the value passed in to the
    390     * {@code borrowMaxWaitMillis} parameter. If the number of instances checked
    391     * out from the sub-pool under the given key is less than
    392     * {@code maxTotalPerKey} and the total number of instances in
    393     * circulation (under all keys) is less than {@code maxTotal}, a new
    394     * instance is created, activated and (if applicable) validated and returned
    395     * to the caller. If validation fails, a {@code NoSuchElementException}
    396     * will be thrown. If the factory returns null when creating an instance,
    397     * a {@code NullPointerException} is thrown.
    398     * </p>
    399     * <p>
    400     * If the associated sub-pool is exhausted (no available idle instances and
    401     * no capacity to create new ones), this method will either block
    402     * ({@link #getBlockWhenExhausted()} is true) or throw a
    403     * {@code NoSuchElementException}
    404     * ({@link #getBlockWhenExhausted()} is false).
    405     * The length of time that this method will block when
    406     * {@link #getBlockWhenExhausted()} is true is determined by the value
    407     * passed in to the {@code borrowMaxWait} parameter.
    408     * </p>
    409     * <p>
    410     * When {@code maxTotal} is set to a positive value and this method is
    411     * invoked when at the limit with no idle instances available under the requested
    412     * key, an attempt is made to create room by clearing the oldest 15% of the
    413     * elements from the keyed sub-pools.
    414     * </p>
    415     * <p>
    416     * When the pool is exhausted, multiple calling threads may be
    417     * simultaneously blocked waiting for instances to become available. A
    418     * "fairness" algorithm has been implemented to ensure that threads receive
    419     * available instances in request arrival order.
    420     * </p>
    421     *
    422     * @param key pool key
    423     * @param maxWaitDuration The time to wait for an object to become
    424     *                        available
    425     *
    426     * @return object instance from the keyed pool
    427     * @throws NoSuchElementException if a keyed object instance cannot be
    428     *                                returned because the pool is exhausted.
    429     *
    430     * @throws Exception if a keyed object instance cannot be returned due to an
    431     *                   error
    432     * @since 2.12.2
    433     */
    434    public T borrowObject(final K key, final Duration maxWaitDuration) throws Exception {
    435        assertOpen();
    436        final Instant startInstant = Instant.now();
    437        Duration remainingWaitDuration = maxWaitDuration;
    438        final AbandonedConfig ac = this.abandonedConfig;
    439        if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 &&
    440                getNumActive() > getMaxTotal() - 3) {
    441            removeAbandoned(ac);
    442        }
    443
    444        PooledObject<T> p = null;
    445
    446        // Get local copy of current config so it is consistent for entire
    447        // method execution
    448        final boolean blockWhenExhausted = getBlockWhenExhausted();
    449
    450        boolean create;
    451        final ObjectDeque<T> objectDeque = register(key);
    452
    453        try {
    454            while (p == null) {
    455                remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant));
    456                create = false;
    457                p = objectDeque.getIdleObjects().pollFirst();
    458                if (p == null) {
    459                    p = create(key, remainingWaitDuration);
    460                    if (PooledObject.nonNull(p)) {
    461                        create = true;
    462                    }
    463                    remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant));
    464                }
    465                if (blockWhenExhausted) {
    466                    if (PooledObject.isNull(p)) {
    467                        p = maxWaitDuration.isNegative() ? objectDeque.getIdleObjects().takeFirst()
    468                                : objectDeque.getIdleObjects().pollFirst(remainingWaitDuration);
    469                    }
    470                    if (PooledObject.isNull(p)) {
    471                        throw new NoSuchElementException(appendStats(
    472                                "Timeout waiting for idle object, borrowMaxWaitMillis=" + maxWaitDuration.toMillis()));
    473                    }
    474                } else if (PooledObject.isNull(p)) {
    475                    throw new NoSuchElementException(appendStats("Pool exhausted"));
    476                }
    477                if (!p.allocate()) {
    478                    p = null;
    479                }
    480
    481                if (PooledObject.nonNull(p)) {
    482                    try {
    483                        factory.activateObject(key, p);
    484                    } catch (final Exception e) {
    485                        try {
    486                            destroy(key, p, true, DestroyMode.NORMAL);
    487                        } catch (final Exception ignored) {
    488                            // ignored - activation failure is more important
    489                        }
    490                        p = null;
    491                        if (create) {
    492                            final NoSuchElementException nsee = new NoSuchElementException(
    493                                    appendStats("Unable to activate object"));
    494                            nsee.initCause(e);
    495                            throw nsee;
    496                        }
    497                    }
    498                    if (PooledObject.nonNull(p) && getTestOnBorrow()) {
    499                        boolean validate = false;
    500                        Throwable validationThrowable = null;
    501                        try {
    502                            validate = factory.validateObject(key, p);
    503                        } catch (final Throwable t) {
    504                            PoolUtils.checkRethrow(t);
    505                            validationThrowable = t;
    506                        }
    507                        if (!validate) {
    508                            try {
    509                                destroy(key, p, true, DestroyMode.NORMAL);
    510                                destroyedByBorrowValidationCount.incrementAndGet();
    511                            } catch (final Exception ignored) {
    512                                // ignored - validation failure is more important
    513                            }
    514                            p = null;
    515                            if (create) {
    516                                final NoSuchElementException nsee = new NoSuchElementException(
    517                                        appendStats("Unable to validate object"));
    518                                nsee.initCause(validationThrowable);
    519                                throw nsee;
    520                            }
    521                        }
    522                    }
    523                }
    524            }
    525        } finally {
    526            deregister(key);
    527        }
    528
    529        updateStatsBorrow(p, Duration.between(startInstant, Instant.now()));
    530
    531        return p.getObject();
    532    }
    533
    534
    535    /**
    536     * Borrows an object from the sub-pool associated with the given key using
    537     * the specified waiting time which only applies if
    538     * {@link #getBlockWhenExhausted()} is true.
    539     * <p>
    540     * If there is one or more idle instances available in the sub-pool
    541     * associated with the given key, then an idle instance will be selected
    542     * based on the value of {@link #getLifo()}, activated and returned.  If
    543     * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to
    544     * {@code true} and validation fails, the instance is destroyed and the
    545     * next available instance is examined.  This continues until either a valid
    546     * instance is returned or there are no more idle instances available.
    547     * </p>
    548     * <p>
    549     * If there are no idle instances available in the sub-pool associated with
    550     * the given key, behavior depends on the {@link #getMaxTotalPerKey()
    551     * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable)
    552     * {@link #getBlockWhenExhausted()} and the value passed in to the
    553     * {@code borrowMaxWaitMillis} parameter. If the number of instances checked
    554     * out from the sub-pool under the given key is less than
    555     * {@code maxTotalPerKey} and the total number of instances in
    556     * circulation (under all keys) is less than {@code maxTotal}, a new
    557     * instance is created, activated and (if applicable) validated and returned
    558     * to the caller. If validation fails, a {@code NoSuchElementException}
    559     * will be thrown. If the factory returns null when creating an instance,
    560     * a {@code NullPointerException} is thrown.
    561     * </p>
    562     * <p>
    563     * If the associated sub-pool is exhausted (no available idle instances and
    564     * no capacity to create new ones), this method will either block
    565     * ({@link #getBlockWhenExhausted()} is true) or throw a
    566     * {@code NoSuchElementException}
    567     * ({@link #getBlockWhenExhausted()} is false).
    568     * The length of time that this method will block when
    569     * {@link #getBlockWhenExhausted()} is true is determined by the value
    570     * passed in to the {@code borrowMaxWait} parameter.
    571     * </p>
    572     * <p>
    573     * When {@code maxTotal} is set to a positive value and this method is
    574     * invoked when at the limit with no idle instances available under the requested
    575     * key, an attempt is made to create room by clearing the oldest 15% of the
    576     * elements from the keyed sub-pools.
    577     * </p>
    578     * <p>
    579     * When the pool is exhausted, multiple calling threads may be
    580     * simultaneously blocked waiting for instances to become available. A
    581     * "fairness" algorithm has been implemented to ensure that threads receive
    582     * available instances in request arrival order.
    583     * </p>
    584     *
    585     * @param key pool key
    586     * @param maxWaitMillis The time to wait in milliseconds for an object to become
    587     *                        available
    588     *
    589     * @return object instance from the keyed pool
    590     * @throws NoSuchElementException if a keyed object instance cannot be
    591     *                                returned because the pool is exhausted.
    592     *
    593     * @throws Exception if a keyed object instance cannot be returned due to an
    594     *                   error
    595     */
    596
    597    public T borrowObject(final K key, final long maxWaitMillis) throws Exception {
    598        return borrowObject(key, Duration.ofMillis(maxWaitMillis));
    599    }
    600
    601    /**
    602     * Calculate the number of objects that need to be created to attempt to
    603     * maintain the minimum number of idle objects while not exceeded the limits
    604     * on the maximum number of objects either per key or totally.
    605     *
    606     * @param objectDeque   The set of objects to check
    607     * @return The number of new objects to create
    608     */
    609    private int calculateDeficit(final ObjectDeque<T> objectDeque) {
    610
    611        if (objectDeque == null) {
    612            return getMinIdlePerKey();
    613        }
    614
    615        // Used more than once so keep a local copy so the value is consistent
    616        final int maxTotal = getMaxTotal();
    617        final int maxTotalPerKeySave = getMaxTotalPerKey();
    618
    619        // Calculate no of objects needed to be created, in order to have
    620        // the number of pooled objects < maxTotalPerKey();
    621        int objectDefecit = getMinIdlePerKey() - objectDeque.getIdleObjects().size();
    622        if (maxTotalPerKeySave > 0) {
    623            final int growLimit = Math.max(0,
    624                    maxTotalPerKeySave - objectDeque.getIdleObjects().size());
    625            objectDefecit = Math.min(objectDefecit, growLimit);
    626        }
    627
    628        // Take the maxTotal limit into account
    629        if (maxTotal > 0) {
    630            final int growLimit = Math.max(0, maxTotal - getNumActive() - getNumIdle());
    631            objectDefecit = Math.min(objectDefecit, growLimit);
    632        }
    633
    634        return objectDefecit;
    635    }
    636
    637    /**
    638     * Clears any objects sitting idle in the pool by removing them from the
    639     * idle instance sub-pools and then invoking the configured
    640     * PoolableObjectFactory's
    641     * {@link KeyedPooledObjectFactory#destroyObject(Object, PooledObject)}
    642     * method on each idle instance.
    643     * <p>
    644     * Implementation notes:
    645     * <ul>
    646     * <li>This method does not destroy or effect in any way instances that are
    647     * checked out when it is invoked.</li>
    648     * <li>Invoking this method does not prevent objects being returned to the
    649     * idle instance pool, even during its execution. Additional instances may
    650     * be returned while removed items are being destroyed.</li>
    651     * <li>Exceptions encountered destroying idle instances are swallowed
    652     * but notified via a {@link SwallowedExceptionListener}.</li>
    653     * </ul>
    654     */
    655    @Override
    656    public void clear() {
    657        poolMap.keySet().forEach(key -> clear(key, false));
    658    }
    659
    660    /**
    661     * Clears the specified sub-pool, removing all pooled instances
    662     * corresponding to the given {@code key}. Exceptions encountered
    663     * destroying idle instances are swallowed but notified via a
    664     * {@link SwallowedExceptionListener}.
    665     * <p>
    666     * If there are clients waiting to borrow objects, this method will
    667     * attempt to reuse the capacity freed by this operation, adding
    668     * instances to the most loaded keyed pools.  To avoid triggering
    669     * possible object creation, use {@link #clear(Object, boolean)}.
    670     *
    671     * @param key the key to clear
    672     */
    673    @Override
    674    public void clear(final K key) {
    675        clear(key, true);
    676    }
    677
    678    /**
    679     * Clears the specified sub-pool, removing all pooled instances
    680     * corresponding to the given {@code key}. Exceptions encountered
    681     * destroying idle instances are swallowed but notified via a
    682     * {@link SwallowedExceptionListener}.
    683     * <p>
    684     * If reuseCapacity is true and there are clients waiting to
    685     * borrow objects, this method will attempt to reuse the capacity freed
    686     * by this operation, adding instances to the most loaded keyed pools.
    687     * </p>
    688     *
    689     * @param key the key to clear
    690     * @param reuseCapacity whether or not to reuse freed capacity
    691     * @since 2.12.0
    692     */
    693    public void clear(final K key, final boolean reuseCapacity) {
    694        // Return immediately if there is no pool under this key.
    695        if (!poolMap.containsKey(key)) {
    696            return;
    697        }
    698        final ObjectDeque<T> objectDeque = register(key);
    699        int freedCapacity = 0;
    700        try {
    701            final BlockingDeque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects();
    702            PooledObject<T> p = idleObjects.poll();
    703            while (p != null) {
    704                try {
    705                    if (destroy(key, p, true, DestroyMode.NORMAL)) {
    706                        freedCapacity++;
    707                    }
    708                } catch (final Exception e) {
    709                    swallowException(e);
    710                }
    711                p = idleObjects.poll();
    712            }
    713        } finally {
    714            deregister(key);
    715        }
    716        if (reuseCapacity) {
    717            reuseCapacity(freedCapacity);
    718        }
    719    }
    720
    721    /**
    722     * Clears oldest 15% of objects in pool.  The method sorts the objects into
    723     * a TreeMap and then iterates the first 15% for removal.
    724     */
    725    public void clearOldest() {
    726
    727        // build sorted map of idle objects
    728        final TreeMap<PooledObject<T>, K> map = new TreeMap<>();
    729
    730        // Each item into the map using the PooledObject object as the
    731        // key. It then gets sorted based on the idle time
    732        poolMap.forEach((key, value) -> value.getIdleObjects().forEach(p -> map.put(p, key)));
    733
    734        // Now iterate created map and kill the first 15% plus one to account
    735        // for zero
    736        int itemsToRemove = (int) (map.size() * 0.15) + 1;
    737        final Iterator<Entry<PooledObject<T>, K>> iter = map.entrySet().iterator();
    738
    739        while (iter.hasNext() && itemsToRemove > 0) {
    740            final Entry<PooledObject<T>, K> entry = iter.next();
    741            // kind of backwards on naming. In the map, each key is the
    742            // PooledObject because it has the ordering with the timestamp
    743            // value. Each value that the key references is the key of the
    744            // list it belongs to.
    745            final K key = entry.getValue();
    746            final PooledObject<T> p = entry.getKey();
    747            // Assume the destruction succeeds
    748            boolean destroyed = true;
    749            try {
    750                destroyed = destroy(key, p, false, DestroyMode.NORMAL);
    751            } catch (final Exception e) {
    752                swallowException(e);
    753            }
    754            if (destroyed) {
    755                itemsToRemove--;
    756            }
    757        }
    758    }
    759
    760    /**
    761     * Closes the keyed object pool. Once the pool is closed,
    762     * {@link #borrowObject(Object)} will fail with IllegalStateException, but
    763     * {@link #returnObject(Object, Object)} and
    764     * {@link #invalidateObject(Object, Object)} will continue to work, with
    765     * returned objects destroyed on return.
    766     * <p>
    767     * Destroys idle instances in the pool by invoking {@link #clear()}.
    768     * </p>
    769     */
    770    @Override
    771    public void close() {
    772        if (isClosed()) {
    773            return;
    774        }
    775
    776        synchronized (closeLock) {
    777            if (isClosed()) {
    778                return;
    779            }
    780
    781            // Stop the evictor before the pool is closed since evict() calls
    782            // assertOpen()
    783            stopEvictor();
    784
    785            closed = true;
    786            // This clear removes any idle objects
    787            clear();
    788
    789            jmxUnregister();
    790
    791            // Release any threads that were waiting for an object
    792            poolMap.values().forEach(e -> e.getIdleObjects().interuptTakeWaiters());
    793            // This clear cleans up the keys now any waiting threads have been
    794            // interrupted
    795            clear();
    796        }
    797    }
    798
    799    /**
    800     * Creates a new pooled object or null.
    801     *
    802     * @param key             Key associated with new pooled object.
    803     * @param maxWaitDuration The time to wait in this method. If negative or ZERO,
    804     *                        this method may wait indefinitely.
    805     * @return The new, wrapped pooled object. May return null.
    806     * @throws Exception If the objection creation fails.
    807     */
    808    private PooledObject<T> create(final K key, final Duration maxWaitDuration) throws Exception {
    809        final Instant startInstant = Instant.now();
    810        Duration remainingWaitDuration = maxWaitDuration.isNegative() ? Duration.ZERO : maxWaitDuration;
    811
    812        int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key
    813        if (maxTotalPerKeySave < 0) {
    814            maxTotalPerKeySave = Integer.MAX_VALUE;
    815        }
    816        final int maxTotal = getMaxTotal();   // All keys
    817
    818        final ObjectDeque<T> objectDeque = poolMap.get(key);
    819
    820        // Check against the overall limit
    821        boolean loop = true;
    822
    823        while (loop) {
    824            final int newNumTotal = numTotal.incrementAndGet();
    825            if (maxTotal > -1 && newNumTotal > maxTotal) {
    826                numTotal.decrementAndGet();
    827                if (getNumIdle() == 0) {
    828                    return null;
    829                }
    830                clearOldest();
    831            } else {
    832                loop = false;
    833            }
    834        }
    835
    836        // Flag that indicates if create should:
    837        // - TRUE:  call the factory to create an object
    838        // - FALSE: return null
    839        // - null:  loop and re-test the condition that determines whether to
    840        //          call the factory
    841        Boolean create = null;
    842        while (create == null) {
    843            remainingWaitDuration = maxWaitDuration.isNegative() ? Duration.ZERO : maxWaitDuration.minus(durationSince(startInstant));
    844            synchronized (objectDeque.makeObjectCountLock) {
    845                final long newCreateCount = objectDeque.getCreateCount().incrementAndGet();
    846                // Check against the per key limit
    847                if (newCreateCount > maxTotalPerKeySave) {
    848                    // The key is currently at capacity or in the process of
    849                    // making enough new objects to take it to capacity.
    850                    objectDeque.getCreateCount().decrementAndGet();
    851                    if (objectDeque.makeObjectCount == 0) {
    852                        // There are no makeObject() calls in progress for this
    853                        // key so the key is at capacity. Do not attempt to
    854                        // create a new object. Return and wait for an object to
    855                        // be returned.
    856                        create = Boolean.FALSE;
    857                    } else // There are makeObject() calls in progress that might
    858                    // bring the pool to capacity. Those calls might also
    859                    // fail so wait until they complete and then re-test if
    860                    // the pool is at capacity or not.
    861                    if (!remainingWaitDuration.isNegative()) {
    862                        objectDeque.makeObjectCountLock.wait(remainingWaitDuration.toMillis(),
    863                                 remainingWaitDuration.getNano() % 1_000_000);
    864                    }
    865                } else {
    866                    // The pool is not at capacity. Create a new object.
    867                    objectDeque.makeObjectCount++;
    868                    create = Boolean.TRUE;
    869                }
    870            }
    871        }
    872
    873        if (!create.booleanValue()) {
    874            numTotal.decrementAndGet();
    875            return null;
    876        }
    877
    878        PooledObject<T> p = null;
    879        try {
    880            p = factory.makeObject(key);
    881            if (PooledObject.isNull(p)) {
    882                numTotal.decrementAndGet();
    883                objectDeque.getCreateCount().decrementAndGet();
    884                throw new NullPointerException(String.format("%s.makeObject() = null", factory.getClass().getSimpleName()));
    885            }
    886            if (getTestOnCreate() && !factory.validateObject(key, p)) {
    887                numTotal.decrementAndGet();
    888                objectDeque.getCreateCount().decrementAndGet();
    889                return null;
    890            }
    891        } catch (final Exception e) {
    892            numTotal.decrementAndGet();
    893            objectDeque.getCreateCount().decrementAndGet();
    894            throw e;
    895        } finally {
    896            synchronized (objectDeque.makeObjectCountLock) {
    897                objectDeque.makeObjectCount--;
    898                objectDeque.makeObjectCountLock.notifyAll();
    899            }
    900        }
    901
    902        final AbandonedConfig ac = this.abandonedConfig;
    903        if (ac != null && ac.getLogAbandoned()) {
    904            p.setLogAbandoned(true);
    905            p.setRequireFullStackTrace(ac.getRequireFullStackTrace());
    906        }
    907
    908        createdCount.incrementAndGet();
    909        objectDeque.getAllObjects().put(IdentityWrapper.unwrap(p), p);
    910        return p;
    911    }
    912
    913    /**
    914     * De-register the use of a key by an object.
    915     * <p>
    916     * {@link #register(Object)} and {@link #deregister(Object)} must always be used as a pair.
    917     * </p>
    918     *
    919     * @param k The key to de-register
    920     */
    921    private void deregister(final K k) {
    922        Lock lock = keyLock.readLock();
    923        try {
    924            lock.lock();
    925            ObjectDeque<T> objectDeque = poolMap.get(k);
    926            if (objectDeque == null) {
    927                throw new IllegalStateException("Attempt to de-register a key for a non-existent pool");
    928            }
    929            final long numInterested = objectDeque.getNumInterested().decrementAndGet();
    930            if (numInterested < 0) {
    931                throw new IllegalStateException("numInterested count for key " + k + " is less than zero");
    932            }
    933            if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) {
    934                // Potential to remove key
    935                // Upgrade to write lock
    936                lock.unlock();
    937                lock = keyLock.writeLock();
    938                lock.lock();
    939                // Pool may have changed since we released the read lock
    940                // numInterested decrement could lead to removal while waiting for write lock
    941                objectDeque = poolMap.get(k);
    942                if (null != objectDeque && objectDeque.getNumInterested().get() == 0 && objectDeque.getCreateCount().get() == 0) {
    943                    // NOTE: Keys must always be removed from both poolMap and
    944                    // poolKeyList at the same time while protected by
    945                    // keyLock.writeLock()
    946                    poolMap.remove(k);
    947                    poolKeyList.remove(k);
    948                }
    949            }
    950        } finally {
    951            lock.unlock();
    952        }
    953    }
    954
    955    /**
    956     * Destroy the wrapped, pooled object.
    957     *
    958     * @param key The key associated with the object to destroy
    959     * @param toDestroy The wrapped object to be destroyed
    960     * @param always Should the object be destroyed even if it is not currently
    961     *               in the set of idle objects for the given key
    962     * @param destroyMode {@link DestroyMode} context provided to the factory
    963     * @return {@code true} if the object was destroyed, otherwise {@code false}
    964     * @throws Exception If the factory throws an exception during destruction
    965     */
    966    private boolean destroy(final K key, final PooledObject<T> toDestroy, final boolean always, final DestroyMode destroyMode) throws Exception {
    967
    968        final ObjectDeque<T> objectDeque = register(key);
    969
    970        try {
    971            boolean isIdle;
    972            synchronized (toDestroy) {
    973                // Check idle state directly
    974                isIdle = toDestroy.getState().equals(PooledObjectState.IDLE);
    975                // If idle, not under eviction test, or always is true, remove instance,
    976                // updating isIdle if instance is found in idle objects
    977                if (isIdle || always) {
    978                    isIdle = objectDeque.getIdleObjects().remove(toDestroy);
    979                }
    980            }
    981            if (isIdle || always) {
    982                objectDeque.getAllObjects().remove(IdentityWrapper.unwrap(toDestroy));
    983                toDestroy.invalidate();
    984
    985                try {
    986                    factory.destroyObject(key, toDestroy, destroyMode);
    987                } finally {
    988                    objectDeque.getCreateCount().decrementAndGet();
    989                    destroyedCount.incrementAndGet();
    990                    numTotal.decrementAndGet();
    991                }
    992                return true;
    993            }
    994            return false;
    995        } finally {
    996            deregister(key);
    997        }
    998    }
    999
    1000    @Override
    1001    void ensureMinIdle() throws Exception {
    1002        final int minIdlePerKeySave = getMinIdlePerKey();
    1003        if (minIdlePerKeySave < 1) {
    1004            return;
    1005        }
    1006
    1007        for (final K k : poolMap.keySet()) {
    1008            ensureMinIdle(k);
    1009        }
    1010    }
    1011
    1012    /**
    1013     * Try to ensure that the configured number of minimum idle objects is available in
    1014     * the pool for the given key.
    1015     * <p>
    1016     * If there is no capacity available to add to the pool, this is a no-op
    1017     * (no exception, no impact to the pool).
    1018     * </p>
    1019     * <p>
    1020     * If the factory returns null when creating an object, a {@code NullPointerException}
    1021     * is thrown.
    1022     * </p>
    1023     *
    1024     * @param key The key to check for idle objects
    1025     * @throws Exception If a new object is required and cannot be created
    1026     */
    1027    private void ensureMinIdle(final K key) throws Exception {
    1028        // Calculate current pool objects
    1029        ObjectDeque<T> objectDeque = poolMap.get(key);
    1030
    1031        // objectDeque == null is OK here. It is handled correctly by both
    1032        // methods called below.
    1033
    1034        // this method isn't synchronized so the
    1035        // calculateDeficit is done at the beginning
    1036        // as a loop limit and a second time inside the loop
    1037        // to stop when another thread already returned the
    1038        // needed objects
    1039        final int deficit = calculateDeficit(objectDeque);
    1040
    1041        for (int i = 0; i < deficit && calculateDeficit(objectDeque) > 0; i++) {
    1042            addObject(key);
    1043            // If objectDeque was null, it won't be any more. Obtain a reference
    1044            // to it so the deficit can be correctly calculated. It needs to
    1045            // take account of objects created in other threads.
    1046            if (objectDeque == null) {
    1047                objectDeque = poolMap.get(key);
    1048            }
    1049        }
    1050    }
    1051
    1052    /**
    1053     * {@inheritDoc}
    1054     * <p>
    1055     * Successive activations of this method examine objects in keyed sub-pools
    1056     * in sequence, cycling through the keys and examining objects in
    1057     * oldest-to-youngest order within the keyed sub-pools.
    1058     * </p>
    1059     */
    1060    @Override
    1061    public void evict() throws Exception {
    1062        assertOpen();
    1063
    1064        if (getNumIdle() > 0) {
    1065
    1066            PooledObject<T> underTest = null;
    1067            final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
    1068
    1069            synchronized (evictionLock) {
    1070                final EvictionConfig evictionConfig = new EvictionConfig(
    1071                        getMinEvictableIdleDuration(),
    1072                        getSoftMinEvictableIdleDuration(),
    1073                        getMinIdlePerKey());
    1074
    1075                final boolean testWhileIdle = getTestWhileIdle();
    1076
    1077                for (int i = 0, m = getNumTests(); i < m; i++) {
    1078                    if (evictionIterator == null || !evictionIterator.hasNext()) {
    1079                        if (evictionKeyIterator == null ||
    1080                                !evictionKeyIterator.hasNext()) {
    1081                            final List<K> keyCopy = new ArrayList<>();
    1082                            final Lock readLock = keyLock.readLock();
    1083                            readLock.lock();
    1084                            try {
    1085                                keyCopy.addAll(poolKeyList);
    1086                            } finally {
    1087                                readLock.unlock();
    1088                            }
    1089                            evictionKeyIterator = keyCopy.iterator();
    1090                        }
    1091                        while (evictionKeyIterator.hasNext()) {
    1092                            evictionKey = evictionKeyIterator.next();
    1093                            final ObjectDeque<T> objectDeque = poolMap.get(evictionKey);
    1094                            if (objectDeque == null) {
    1095                                continue;
    1096                            }
    1097
    1098                            final Deque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects();
    1099                            evictionIterator = new EvictionIterator(idleObjects);
    1100                            if (evictionIterator.hasNext()) {
    1101                                break;
    1102                            }
    1103                            evictionIterator = null;
    1104                        }
    1105                    }
    1106                    if (evictionIterator == null) {
    1107                        // Pools exhausted
    1108                        return;
    1109                    }
    1110                    final Deque<PooledObject<T>> idleObjects;
    1111                    try {
    1112                        underTest = evictionIterator.next();
    1113                        idleObjects = evictionIterator.getIdleObjects();
    1114                    } catch (final NoSuchElementException nsee) {
    1115                        // Object was borrowed in another thread
    1116                        // Don't count this as an eviction test so reduce i;
    1117                        i--;
    1118                        evictionIterator = null;
    1119                        continue;
    1120                    }
    1121
    1122                    if (!underTest.startEvictionTest()) {
    1123                        // Object was borrowed in another thread
    1124                        // Don't count this as an eviction test so reduce i;
    1125                        i--;
    1126                        continue;
    1127                    }
    1128
    1129                    // User provided eviction policy could throw all sorts of
    1130                    // crazy exceptions. Protect against such an exception
    1131                    // killing the eviction thread.
    1132                    boolean evict;
    1133                    try {
    1134                        evict = evictionPolicy.evict(evictionConfig, underTest,
    1135                                poolMap.get(evictionKey).getIdleObjects().size());
    1136                    } catch (final Throwable t) {
    1137                        // Slightly convoluted as SwallowedExceptionListener
    1138                        // uses Exception rather than Throwable
    1139                        PoolUtils.checkRethrow(t);
    1140                        swallowException(new Exception(t));
    1141                        // Don't evict on error conditions
    1142                        evict = false;
    1143                    }
    1144
    1145                    if (evict) {
    1146                        destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
    1147                        destroyedByEvictorCount.incrementAndGet();
    1148                    } else {
    1149                        if (testWhileIdle) {
    1150                            boolean active = false;
    1151                            try {
    1152                                factory.activateObject(evictionKey, underTest);
    1153                                active = true;
    1154                            } catch (final Exception e) {
    1155                                destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
    1156                                destroyedByEvictorCount.incrementAndGet();
    1157                            }
    1158                            if (active) {
    1159                                boolean validate = false;
    1160                                Throwable validationThrowable = null;
    1161                                try {
    1162                                    validate = factory.validateObject(evictionKey, underTest);
    1163                                } catch (final Throwable t) {
    1164                                    PoolUtils.checkRethrow(t);
    1165                                    validationThrowable = t;
    1166                                }
    1167                                if (!validate) {
    1168                                    destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
    1169                                    destroyedByEvictorCount.incrementAndGet();
    1170                                    if (validationThrowable != null) {
    1171                                        if (validationThrowable instanceof RuntimeException) {
    1172                                            throw (RuntimeException) validationThrowable;
    1173                                        }
    1174                                        throw (Error) validationThrowable;
    1175                                    }
    1176                                } else {
    1177                                    try {
    1178                                        factory.passivateObject(evictionKey, underTest);
    1179                                    } catch (final Exception e) {
    1180                                        destroy(evictionKey, underTest, true, DestroyMode.NORMAL);
    1181                                        destroyedByEvictorCount.incrementAndGet();
    1182                                    }
    1183                                }
    1184                            }
    1185                        }
    1186                        underTest.endEvictionTest(idleObjects);
    1187                    }
    1188                }
    1189            }
    1190        }
    1191        final AbandonedConfig ac = this.abandonedConfig;
    1192        if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
    1193            removeAbandoned(ac);
    1194        }
    1195        if (reuseCapacityOnMaintenance) {
    1196            reuseCapacity();
    1197        }
    1198    }
    1199
    1200    /**
    1201     * Gets a reference to the factory used to create, destroy and validate
    1202     * the objects used by this pool.
    1203     *
    1204     * @return the factory
    1205     */
    1206    public KeyedPooledObjectFactory<K, T> getFactory() {
    1207        return factory;
    1208    }
    1209
    1210    /**
    1211     * Gets a copy of the pool key list.
    1212     *
    1213     * @return a copy of the pool key list.
    1214     * @since 2.12.0
    1215     */
    1216    @Override
    1217    @SuppressWarnings("unchecked")
    1218    public List<K> getKeys() {
    1219        return (List<K>) poolKeyList.clone();
    1220    }
    1221
    1222    /**
    1223     * Gets the cap on the number of "idle" instances per key in the pool.
    1224     * If maxIdlePerKey is set too low on heavily loaded systems it is possible
    1225     * you will see objects being destroyed and almost immediately new objects
    1226     * being created. This is a result of the active threads momentarily
    1227     * returning objects faster than they are requesting them, causing the
    1228     * number of idle objects to rise above maxIdlePerKey. The best value for
    1229     * maxIdlePerKey for heavily loaded system will vary but the default is a
    1230     * good starting point.
    1231     *
    1232     * @return the maximum number of "idle" instances that can be held in a
    1233     *         given keyed sub-pool or a negative value if there is no limit
    1234     *
    1235     * @see #setMaxIdlePerKey
    1236     */
    1237    @Override
    1238    public int getMaxIdlePerKey() {
    1239        return maxIdlePerKey;
    1240    }
    1241
    1242    /**
    1243     * Gets the limit on the number of object instances allocated by the pool
    1244     * (checked out or idle), per key. When the limit is reached, the sub-pool
    1245     * is said to be exhausted. A negative value indicates no limit.
    1246     *
    1247     * @return the limit on the number of active instances per key
    1248     * @see #setMaxTotalPerKey
    1249     */
    1250    @Override
    1251    public int getMaxTotalPerKey() {
    1252        return maxTotalPerKey;
    1253    }
    1254
    1255    /**
    1256     * Gets the target for the minimum number of idle objects to maintain in
    1257     * each of the keyed sub-pools. This setting only has an effect if it is
    1258     * positive and {@link #getDurationBetweenEvictionRuns()} is greater than
    1259     * zero. If this is the case, an attempt is made to ensure that each
    1260     * sub-pool has the required minimum number of instances during idle object
    1261     * eviction runs.
    1262     * <p>
    1263     * If the configured value of minIdlePerKey is greater than the configured
    1264     * value for maxIdlePerKey then the value of maxIdlePerKey will be used
    1265     * instead.
    1266     * </p>
    1267     *
    1268     * @return minimum size of the each keyed pool
    1269     * @see #setTimeBetweenEvictionRunsMillis
    1270     */
    1271    @Override
    1272    public int getMinIdlePerKey() {
    1273        final int maxIdlePerKeySave = getMaxIdlePerKey();
    1274        return Math.min(this.minIdlePerKey, maxIdlePerKeySave);
    1275    }
    1276
    1277    @Override
    1278    public int getNumActive() {
    1279        return numTotal.get() - getNumIdle();
    1280    }
    1281
    1282    @Override
    1283    public int getNumActive(final K key) {
    1284        final ObjectDeque<T> objectDeque = poolMap.get(key);
    1285        if (objectDeque != null) {
    1286            return objectDeque.getAllObjects().size() -
    1287                    objectDeque.getIdleObjects().size();
    1288        }
    1289        return 0;
    1290    }
    1291
    1292    @Override
    1293    public Map<String, Integer> getNumActivePerKey() {
    1294        return poolMap.entrySet().stream().collect(Collectors.toMap(
    1295                e -> e.getKey().toString(),
    1296                e -> Integer.valueOf(e.getValue().getAllObjects().size() - e.getValue().getIdleObjects().size()),
    1297                (t, u) -> u));
    1298    }
    1299
    1300    @Override
    1301    public int getNumIdle() {
    1302        return poolMap.values().stream().mapToInt(e -> e.getIdleObjects().size()).sum();
    1303    }
    1304
    1305    @Override
    1306    public int getNumIdle(final K key) {
    1307        final ObjectDeque<T> objectDeque = poolMap.get(key);
    1308        return objectDeque != null ? objectDeque.getIdleObjects().size() : 0;
    1309    }
    1310
    1311    /**
    1312     * Gets the number of objects to test in a run of the idle object
    1313     * evictor.
    1314     *
    1315     * @return The number of objects to test for validity
    1316     */
    1317    private int getNumTests() {
    1318        final int totalIdle = getNumIdle();
    1319        final int numTests = getNumTestsPerEvictionRun();
    1320        if (numTests >= 0) {
    1321            return Math.min(numTests, totalIdle);
    1322        }
    1323        return (int) Math.ceil(totalIdle / Math.abs((double) numTests));
    1324    }
    1325
    1326    /**
    1327     * Gets an estimate of the number of threads currently blocked waiting for
    1328     * an object from the pool. This is intended for monitoring only, not for
    1329     * synchronization control.
    1330     *
    1331     * @return The estimate of the number of threads currently blocked waiting
    1332     *         for an object from the pool
    1333     */
    1334    @Override
    1335    public int getNumWaiters() {
    1336        if (getBlockWhenExhausted()) {
    1337            // Assume no overflow
    1338            return poolMap.values().stream().mapToInt(e -> e.getIdleObjects().getTakeQueueLength()).sum();
    1339        }
    1340        return 0;
    1341    }
    1342
    1343    /**
    1344     * Gets an estimate of the number of threads currently blocked waiting for
    1345     * an object from the pool for each key. This is intended for
    1346     * monitoring only, not for synchronization control.
    1347     *
    1348     * @return The estimate of the number of threads currently blocked waiting
    1349     *         for an object from the pool for each key
    1350     */
    1351    @Override
    1352    public Map<String, Integer> getNumWaitersByKey() {
    1353        final Map<String, Integer> result = new HashMap<>();
    1354        poolMap.forEach((k, deque) -> result.put(k.toString(), getBlockWhenExhausted() ?
    1355                Integer.valueOf(deque.getIdleObjects().getTakeQueueLength()) :
    1356                ZERO));
    1357        return result;
    1358    }
    1359
    1360    /**
    1361     * Gets whether to call {@link #reuseCapacity()} during pool maintenance (eviction).
    1362     * When true, the pool will attempt to reuse freed capacity at the end of each
    1363     * eviction run.
    1364     *
    1365     * @return {@code true} if capacity reuse is enabled during maintenance, {@code false} otherwise
    1366     * @see #setReuseCapacityOnMaintenance(boolean)
    1367     * @since 2.13.0
    1368     */
    1369    public boolean getReuseCapacityOnMaintenance() {
    1370        return reuseCapacityOnMaintenance;
    1371    }
    1372
    1373    /**
    1374     * Gets whether to call {@link #reuseCapacity()} when returning objects to the pool.
    1375     * When true, the pool will check if there are threads waiting to borrow objects
    1376     * and attempt to reuse the capacity freed by the return operation.
    1377     *
    1378     * @return {@code true} if capacity reuse is enabled on return, {@code false} otherwise
    1379     * @see #setReuseCapacityOnReturn(boolean)
    1380     * @since 2.13.0
    1381     */
    1382    public boolean getReuseCapacityOnReturn() {
    1383        return reuseCapacityOnReturn;
    1384    }
    1385
    1386    @Override
    1387    String getStatsString() {
    1388        // Simply listed in AB order.
    1389        return super.getStatsString() +
    1390                String.format(", fairness=%s, maxIdlePerKey%,d, maxTotalPerKey=%,d, minIdlePerKey=%,d, numTotal=%,d",
    1391                        fairness, maxIdlePerKey, maxTotalPerKey, minIdlePerKey, numTotal.get());
    1392    }
    1393
    1394    /**
    1395     * Tests to see if there are any threads currently waiting to borrow
    1396     * objects but are blocked waiting for more objects to become available.
    1397     *
    1398     * @return {@code true} if there is at least one thread waiting otherwise
    1399     *         {@code false}
    1400     */
    1401    private boolean hasBorrowWaiters() {
    1402        return getBlockWhenExhausted() && poolMap.values().stream().anyMatch(deque -> deque.getIdleObjects().hasTakeWaiters());
    1403    }
    1404
    1405    /**
    1406     * {@inheritDoc}
    1407     * <p>
    1408     * Activation of this method decrements the active count associated with
    1409     * the given keyed pool and attempts to destroy {@code obj.}
    1410     * </p>
    1411     *
    1412     * @param key pool key
    1413     * @param obj instance to invalidate
    1414     * @throws Exception             if an exception occurs destroying the
    1415     *                               object
    1416     * @throws IllegalStateException if obj does not belong to the pool
    1417     *                               under the given key
    1418     */
    1419    @Override
    1420    public void invalidateObject(final K key, final T obj) throws Exception {
    1421        invalidateObject(key, obj, DestroyMode.NORMAL);
    1422    }
    1423
    1424    /**
    1425     * {@inheritDoc}
    1426     * <p>
    1427     * Activation of this method decrements the active count associated with
    1428     * the given keyed pool and attempts to destroy {@code obj.}
    1429     * </p>
    1430     *
    1431     * @param key pool key
    1432     * @param obj instance to invalidate
    1433     * @param destroyMode DestroyMode context provided to factory
    1434     * @throws Exception             if an exception occurs destroying the
    1435     *                               object
    1436     * @throws IllegalStateException if obj does not belong to the pool
    1437     *                               under the given key
    1438     * @since 2.9.0
    1439     */
    1440    @Override
    1441    public void invalidateObject(final K key, final T obj, final DestroyMode destroyMode) throws Exception {
    1442        final ObjectDeque<T> objectDeque = poolMap.get(key);
    1443        final PooledObject<T> p = objectDeque != null ? objectDeque.getAllObjects().get(new IdentityWrapper<>(obj)) : null;
    1444        if (p == null) {
    1445            throw new IllegalStateException(appendStats("Object not currently part of this pool"));
    1446        }
    1447        synchronized (p) {
    1448            if (p.getState() != PooledObjectState.INVALID) {
    1449                destroy(key, p, true, destroyMode);
    1450                reuseCapacity();
    1451            }
    1452        }
    1453    }
    1454
    1455    /**
    1456     * Provides information on all the objects in the pool, both idle (waiting
    1457     * to be borrowed) and active (currently borrowed).
    1458     * <p>
    1459     * Note: This is named listAllObjects so it is presented as an operation via
    1460     * JMX. That means it won't be invoked unless explicitly requested
    1461     * whereas all attributes will be automatically requested when viewing the
    1462     * attributes for an object in a tool like JConsole.
    1463     * </p>
    1464     *
    1465     * @return Information grouped by key on all the objects in the pool
    1466     */
    1467    @Override
    1468    public Map<String, List<DefaultPooledObjectInfo>> listAllObjects() {
    1469        return poolMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toString(),
    1470                e -> e.getValue().getAllObjects().values().stream().map(DefaultPooledObjectInfo::new).collect(Collectors.toList())));
    1471    }
    1472
    1473    /**
    1474     * Registers a key for pool control and ensures that
    1475     * {@link #getMinIdlePerKey()} idle instances are created.
    1476     *
    1477     * @param key   The key to register for pool control.
    1478     * @throws Exception If the associated factory throws an exception
    1479     */
    1480    public void preparePool(final K key) throws Exception {
    1481        final int minIdlePerKeySave = getMinIdlePerKey();
    1482        if (minIdlePerKeySave < 1) {
    1483            return;
    1484        }
    1485        ensureMinIdle(key);
    1486    }
    1487
    1488    /**
    1489     * Register the use of a key by an object.
    1490     * <p>
    1491     * {@link #register(Object)} and {@link #deregister(Object)} must always be used as a pair.
    1492     * </p>
    1493     *
    1494     * @param k The key to register
    1495     * @return The objects currently associated with the given key. If this
    1496     *         method returns without throwing an exception then it will never
    1497     *         return null.
    1498     */
    1499    private ObjectDeque<T> register(final K k) {
    1500        Lock lock = keyLock.readLock();
    1501        ObjectDeque<T> objectDeque = null;
    1502        try {
    1503            lock.lock();
    1504            objectDeque = poolMap.get(k);
    1505            if (objectDeque == null) {
    1506                // Upgrade to write lock
    1507                lock.unlock();
    1508                lock = keyLock.writeLock();
    1509                lock.lock();
    1510                final AtomicBoolean allocated = new AtomicBoolean();
    1511                objectDeque = poolMap.computeIfAbsent(k, key -> {
    1512                    allocated.set(true);
    1513                    final ObjectDeque<T> deque = new ObjectDeque<>(fairness);
    1514                    deque.getNumInterested().incrementAndGet();
    1515                    // NOTE: Keys must always be added to both poolMap and
    1516                    //       poolKeyList at the same time while protected by
    1517                    //       the write lock
    1518                    poolKeyList.add(k);
    1519                    return deque;
    1520                });
    1521                if (!allocated.get()) {
    1522                    // Another thread could have beaten us to creating the deque, while we were
    1523                    // waiting for the write lock, so re-get it from the map.
    1524                    objectDeque = poolMap.get(k);
    1525                    objectDeque.getNumInterested().incrementAndGet();
    1526                }
    1527            } else {
    1528                objectDeque.getNumInterested().incrementAndGet();
    1529            }
    1530        } finally {
    1531            lock.unlock();
    1532        }
    1533        return objectDeque;
    1534    }
    1535
    1536    /**
    1537     * Recovers abandoned objects which have been checked out but
    1538     * not used since longer than the removeAbandonedTimeout.
    1539     *
    1540     * @param abandonedConfig The configuration to use to identify abandoned objects
    1541     */
    1542    @SuppressWarnings("resource") // The PrintWriter is managed elsewhere
    1543    private void removeAbandoned(final AbandonedConfig abandonedConfig) {
    1544        poolMap.forEach((key, value) -> {
    1545            // Generate a list of abandoned objects to remove
    1546            final ArrayList<PooledObject<T>> remove = createRemoveList(abandonedConfig, value.getAllObjects());
    1547            // Now remove the abandoned objects
    1548            remove.forEach(pooledObject -> {
    1549                if (abandonedConfig.getLogAbandoned()) {
    1550                    pooledObject.printStackTrace(abandonedConfig.getLogWriter());
    1551                }
    1552                try {
    1553                    invalidateObject(key, pooledObject.getObject(), DestroyMode.ABANDONED);
    1554                } catch (final Exception e) {
    1555                    swallowException(e);
    1556                }
    1557            });
    1558        });
    1559    }
    1560
    1561    /**
    1562     * Returns an object to a keyed sub-pool.
    1563     * <p>
    1564     * If {@link #getMaxIdlePerKey() maxIdle} is set to a positive value and the
    1565     * number of idle instances under the given key has reached this value, the
    1566     * returning instance is destroyed.
    1567     * </p>
    1568     * <p>
    1569     * If {@link #getTestOnReturn() testOnReturn} == true, the returning
    1570     * instance is validated before being returned to the idle instance sub-pool
    1571     * under the given key. In this case, if validation fails, the instance is
    1572     * destroyed.
    1573     * </p>
    1574     * <p>
    1575     * Exceptions encountered destroying objects for any reason are swallowed
    1576     * but notified via a {@link SwallowedExceptionListener}.
    1577     * </p>
    1578     *
    1579     * @param key pool key
    1580     * @param obj instance to return to the keyed pool
    1581     * @throws IllegalStateException if an object is returned to the pool that
    1582     *                               was not borrowed from it or if an object is
    1583     *                               returned to the pool multiple times
    1584     */
    1585    @Override
    1586    public void returnObject(final K key, final T obj) {
    1587
    1588        final ObjectDeque<T> objectDeque = poolMap.get(key);
    1589
    1590        if (objectDeque == null) {
    1591            throw new IllegalStateException("No keyed pool found under the given key.");
    1592        }
    1593
    1594        final PooledObject<T> p = objectDeque.getAllObjects().get(new IdentityWrapper<>(obj));
    1595
    1596        if (PooledObject.isNull(p)) {
    1597            throw new IllegalStateException("Returned object not currently part of this pool");
    1598        }
    1599
    1600        markReturningState(p);
    1601
    1602        final Duration activeTime = p.getActiveDuration();
    1603
    1604        try {
    1605            if (getTestOnReturn() && !factory.validateObject(key, p)) {
    1606                try {
    1607                    destroy(key, p, true, DestroyMode.NORMAL);
    1608                } catch (final Exception e) {
    1609                    swallowException(e);
    1610                }
    1611                whenWaitersAddObject(key, objectDeque.idleObjects);
    1612                return;
    1613            }
    1614
    1615            try {
    1616                factory.passivateObject(key, p);
    1617            } catch (final Exception e1) {
    1618                swallowException(e1);
    1619                try {
    1620                    destroy(key, p, true, DestroyMode.NORMAL);
    1621                } catch (final Exception e) {
    1622                    swallowException(e);
    1623                }
    1624                whenWaitersAddObject(key, objectDeque.idleObjects);
    1625                return;
    1626            }
    1627
    1628            if (!p.deallocate()) {
    1629                throw new IllegalStateException("Object has already been returned to this pool");
    1630            }
    1631
    1632            final int maxIdle = getMaxIdlePerKey();
    1633            final BlockingDeque<PooledObject<T>> idleObjects = objectDeque.getIdleObjects();
    1634
    1635            if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) {
    1636                try {
    1637                    destroy(key, p, true, DestroyMode.NORMAL);
    1638                } catch (final Exception e) {
    1639                    swallowException(e);
    1640                }
    1641            } else {
    1642                if (getLifo()) {
    1643                    idleObjects.addFirst(p);
    1644                } else {
    1645                    idleObjects.addLast(p);
    1646                }
    1647                if (isClosed()) {
    1648                    // Pool closed while object was being added to idle objects.
    1649                    // Make sure the returned object is destroyed rather than left
    1650                    // in the idle object pool (which would effectively be a leak)
    1651                    clear(key);
    1652                }
    1653            }
    1654        } finally {
    1655            if (reuseCapacityOnReturn && hasBorrowWaiters()) {
    1656                reuseCapacity();
    1657            }
    1658            updateStatsReturn(activeTime);
    1659        }
    1660    }
    1661
    1662    /**
    1663     * Attempt to create one new instance to serve from the most heavily
    1664     * loaded pool that can add a new instance.
    1665     *
    1666     * This method exists to ensure liveness in the pool when threads are
    1667     * parked waiting and capacity to create instances under the requested keys
    1668     * subsequently becomes available.
    1669     *
    1670     * This method is not guaranteed to create an instance and its selection
    1671     * of the most loaded pool that can create an instance may not always be
    1672     * correct, since it does not lock the pool and instances may be created,
    1673     * borrowed, returned or destroyed by other threads while it is executing.
    1674     */
    1675    private void reuseCapacity() {
    1676        final int maxTotalPerKeySave = getMaxTotalPerKey();
    1677        int maxQueueLength = 0;
    1678        BlockingDeque<PooledObject<T>> mostLoadedPool = null;
    1679        K mostLoadedKey = null;
    1680
    1681        // Find the most loaded pool that could take a new instance
    1682        for (final Map.Entry<K, ObjectDeque<T>> entry : poolMap.entrySet()) {
    1683            final K k = entry.getKey();
    1684            final LinkedBlockingDeque<PooledObject<T>> pool = entry.getValue().getIdleObjects();
    1685            final int queueLength = pool.getTakeQueueLength();
    1686            if (getNumActive(k) < maxTotalPerKeySave && queueLength > maxQueueLength) {
    1687                maxQueueLength = queueLength;
    1688                mostLoadedPool = pool;
    1689                mostLoadedKey = k;
    1690            }
    1691        }
    1692
    1693        // Attempt to add an instance to the most loaded pool.
    1694        if (mostLoadedPool != null) {
    1695            register(mostLoadedKey);
    1696            try {
    1697                // If there is no capacity to add, create will return null
    1698                // and addIdleObject will no-op.
    1699                addIdleObject(mostLoadedKey, create(mostLoadedKey, Duration.ZERO));
    1700            } catch (final Exception e) {
    1701                swallowException(e);
    1702            } finally {
    1703                deregister(mostLoadedKey);
    1704            }
    1705        }
    1706    }
    1707
    1708    /**
    1709     * Call {@link #reuseCapacity()} repeatedly.
    1710     * <p>
    1711     * Always activates {@link #reuseCapacity()} at least once.
    1712     *
    1713     * @param newCapacity number of times to call {@link #reuseCapacity()}
    1714     */
    1715    private void reuseCapacity(final int newCapacity) {
    1716        final int bound = newCapacity < 1 ? 1 : newCapacity;
    1717        for (int i = 0; i < bound; i++) {
    1718            reuseCapacity();
    1719        }
    1720    }
    1721
    1722    /**
    1723     * Sets the configuration.
    1724     *
    1725     * @param conf the new configuration to use. This is used by value.
    1726     * @see GenericKeyedObjectPoolConfig
    1727     */
    1728    public void setConfig(final GenericKeyedObjectPoolConfig<T> conf) {
    1729        super.setConfig(conf);
    1730        setMaxIdlePerKey(conf.getMaxIdlePerKey());
    1731        setMaxTotalPerKey(conf.getMaxTotalPerKey());
    1732        setMaxTotal(conf.getMaxTotal());
    1733        setMinIdlePerKey(conf.getMinIdlePerKey());
    1734        setReuseCapacityOnReturn(conf.getReuseCapacityOnReturn());
    1735        setReuseCapacityOnMaintenance(conf.getReuseCapacityOnMaintenance());
    1736    }
    1737
    1738    /**
    1739     * Sets the cap on the number of "idle" instances per key in the pool.
    1740     * If maxIdlePerKey is set too low on heavily loaded systems it is possible
    1741     * you will see objects being destroyed and almost immediately new objects
    1742     * being created. This is a result of the active threads momentarily
    1743     * returning objects faster than they are requesting them, causing the
    1744     * number of idle objects to rise above maxIdlePerKey. The best value for
    1745     * maxIdlePerKey for heavily loaded system will vary but the default is a
    1746     * good starting point.
    1747     *
    1748     * @param maxIdlePerKey the maximum number of "idle" instances that can be
    1749     *                      held in a given keyed sub-pool. Use a negative value
    1750     *                      for no limit
    1751     *
    1752     * @see #getMaxIdlePerKey
    1753     */
    1754    public void setMaxIdlePerKey(final int maxIdlePerKey) {
    1755        this.maxIdlePerKey = maxIdlePerKey;
    1756    }
    1757
    1758    /**
    1759     * Sets the limit on the number of object instances allocated by the pool
    1760     * (checked out or idle), per key. When the limit is reached, the sub-pool
    1761     * is said to be exhausted. A negative value indicates no limit.
    1762     *
    1763     * @param maxTotalPerKey the limit on the number of active instances per key
    1764     * @see #getMaxTotalPerKey
    1765     */
    1766    public void setMaxTotalPerKey(final int maxTotalPerKey) {
    1767        this.maxTotalPerKey = maxTotalPerKey;
    1768    }
    1769
    1770    /**
    1771     * Sets the target for the minimum number of idle objects to maintain in
    1772     * each of the keyed sub-pools. This setting only has an effect if it is
    1773     * positive and {@link #getDurationBetweenEvictionRuns()} is greater than
    1774     * zero. If this is the case, an attempt is made to ensure that each
    1775     * sub-pool has the required minimum number of instances during idle object
    1776     * eviction runs.
    1777     * <p>
    1778     * If the configured value of minIdlePerKey is greater than the configured
    1779     * value for maxIdlePerKey then the value of maxIdlePerKey will be used
    1780     * instead.
    1781     * </p>
    1782     *
    1783     * @param minIdlePerKey The minimum size of the each keyed pool
    1784     * @see #getMinIdlePerKey()
    1785     * @see #getMaxIdlePerKey()
    1786     * @see #setDurationBetweenEvictionRuns(Duration)
    1787     */
    1788    public void setMinIdlePerKey(final int minIdlePerKey) {
    1789        this.minIdlePerKey = minIdlePerKey;
    1790    }
    1791
    1792    /**
    1793     * Sets whether to call {@link #reuseCapacity()} during pool maintenance (eviction).
    1794     * When enabled, the pool will attempt to reuse capacity at the end of each
    1795     * eviction run.
    1796     *
    1797     * @param reuseCapacityOnMaintenance {@code true} to enable capacity reuse during
    1798     *                                   maintenance, {@code false} to disable
    1799     * @see #getReuseCapacityOnMaintenance()
    1800     * @since 2.13.0
    1801     */
    1802    public void setReuseCapacityOnMaintenance(final boolean reuseCapacityOnMaintenance) {
    1803        this.reuseCapacityOnMaintenance = reuseCapacityOnMaintenance;
    1804    }
    1805
    1806    /**
    1807     * Sets whether to call {@link #reuseCapacity()} when returning objects to the pool.
    1808     * When enabled, the pool will check if there are threads waiting to borrow objects
    1809     * and attempt to reuse capacity (across pools) on return.
    1810     *
    1811     * @param reuseCapacityOnReturn {@code true} to enable capacity reuse on return,
    1812     *                              {@code false} to disable
    1813     * @see #getReuseCapacityOnReturn()
    1814     * @since 2.13.0
    1815     */
    1816    public void setReuseCapacityOnReturn(final boolean reuseCapacityOnReturn) {
    1817        this.reuseCapacityOnReturn = reuseCapacityOnReturn;
    1818    }
    1819
    1820    @Override
    1821    protected void toStringAppendFields(final StringBuilder builder) {
    1822        super.toStringAppendFields(builder);
    1823        builder.append(", maxIdlePerKey=");
    1824        builder.append(maxIdlePerKey);
    1825        builder.append(", minIdlePerKey=");
    1826        builder.append(minIdlePerKey);
    1827        builder.append(", maxTotalPerKey=");
    1828        builder.append(maxTotalPerKey);
    1829        builder.append(", factory=");
    1830        builder.append(factory);
    1831        builder.append(", fairness=");
    1832        builder.append(fairness);
    1833        builder.append(", poolMap=");
    1834        builder.append(poolMap);
    1835        builder.append(", poolKeyList=");
    1836        builder.append(poolKeyList);
    1837        builder.append(", keyLock=");
    1838        builder.append(keyLock);
    1839        builder.append(", numTotal=");
    1840        builder.append(numTotal);
    1841        builder.append(", evictionKeyIterator=");
    1842        builder.append(evictionKeyIterator);
    1843        builder.append(", evictionKey=");
    1844        builder.append(evictionKey);
    1845        builder.append(", abandonedConfig=");
    1846        builder.append(abandonedConfig);
    1847        builder.append(", reuseCapacityOnReturn=");
    1848        builder.append(reuseCapacityOnReturn);
    1849        builder.append(", reuseCapacityOnMaintenance=");
    1850        builder.append(reuseCapacityOnMaintenance);
    1851    }
    1852
    1853    /**
    1854     * @since 2.10.0
    1855     */
    1856    @Override
    1857    public void use(final T pooledObject) {
    1858        final AbandonedConfig abandonedCfg = this.abandonedConfig;
    1859        if (abandonedCfg != null && abandonedCfg.getUseUsageTracking()) {
    1860            poolMap.values().stream()
    1861                .map(pool -> pool.getAllObjects().get(new IdentityWrapper<>(pooledObject)))
    1862                .filter(Objects::nonNull)
    1863                .findFirst()
    1864                .ifPresent(PooledObject::use);
    1865        }
    1866    }
    1867
    1868    /**
    1869     * When there is at least one thread waiting on the given deque, try to add an instance to pool under the given key.
    1870     * NOTE: there is no check that the key corresponds to the deque (it is assumed that the key was used to get the deque
    1871     * from the poolMap)
    1872     *
    1873     * @param key pool key.
    1874     * @param idleObjects deque backing the pool under the given key
    1875     */
    1876    private void whenWaitersAddObject(final K key, final LinkedBlockingDeque<PooledObject<T>> idleObjects) {
    1877        if (idleObjects.hasTakeWaiters()) {
    1878            try {
    1879                addObject(key);
    1880            } catch (final Exception e) {
    1881                swallowException(e);
    1882            }
    1883        }
    1884    }
    1885
    1886}