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