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