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