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