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