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