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