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