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