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