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