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; 018 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.Iterator; 022import java.util.Map; 023import java.util.NoSuchElementException; 024import java.util.Timer; 025import java.util.TimerTask; 026import java.util.Collections; 027import java.util.concurrent.locks.ReentrantReadWriteLock; 028import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; 029import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; 030 031/** 032 * This class consists exclusively of static methods that operate on or return 033 * ObjectPool or KeyedObjectPool related interfaces. 034 * 035 * @version $Revision: 1552588 $ 036 * 037 * @since 2.0 038 */ 039public final class PoolUtils { 040 041 /** 042 * Timer used to periodically check pools idle object count. Because a 043 * {@link Timer} creates a {@link Thread}, an IODH is used. 044 */ 045 static class TimerHolder { 046 static final Timer MIN_IDLE_TIMER = new Timer(true); 047 } 048 049 /** 050 * PoolUtils instances should NOT be constructed in standard programming. 051 * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);. 052 * This constructor is public to permit tools that require a JavaBean 053 * instance to operate. 054 */ 055 public PoolUtils() { 056 } 057 058 /** 059 * Should the supplied Throwable be re-thrown (eg if it is an instance of 060 * one of the Throwables that should never be swallowed). Used by the pool 061 * error handling for operations that throw exceptions that normally need to 062 * be ignored. 063 * 064 * @param t 065 * The Throwable to check 066 * @throws ThreadDeath 067 * if that is passed in 068 * @throws VirtualMachineError 069 * if that is passed in 070 */ 071 public static void checkRethrow(Throwable t) { 072 if (t instanceof ThreadDeath) { 073 throw (ThreadDeath) t; 074 } 075 if (t instanceof VirtualMachineError) { 076 throw (VirtualMachineError) t; 077 } 078 // All other instances of Throwable will be silently swallowed 079 } 080 081 /** 082 * Periodically check the idle object count for the pool. At most one idle 083 * object will be added per period. If there is an exception when calling 084 * {@link ObjectPool#addObject()} then no more checks will be performed. 085 * 086 * @param pool 087 * the pool to check periodically. 088 * @param minIdle 089 * if the {@link ObjectPool#getNumIdle()} is less than this then 090 * add an idle object. 091 * @param period 092 * the frequency to check the number of idle objects in a pool, 093 * see {@link Timer#schedule(TimerTask, long, long)}. 094 * @param <T> the type of objects in the pool 095 * @return the {@link TimerTask} that will periodically check the pools idle 096 * object count. 097 * @throws IllegalArgumentException 098 * when <code>pool</code> is <code>null</code> or when 099 * <code>minIdle</code> is negative or when <code>period</code> 100 * isn't valid for {@link Timer#schedule(TimerTask, long, long)} 101 */ 102 public static <T> TimerTask checkMinIdle(final ObjectPool<T> pool, 103 final int minIdle, final long period) 104 throws IllegalArgumentException { 105 if (pool == null) { 106 throw new IllegalArgumentException("keyedPool must not be null."); 107 } 108 if (minIdle < 0) { 109 throw new IllegalArgumentException("minIdle must be non-negative."); 110 } 111 final TimerTask task = new ObjectPoolMinIdleTimerTask<T>(pool, minIdle); 112 getMinIdleTimer().schedule(task, 0L, period); 113 return task; 114 } 115 116 /** 117 * Periodically check the idle object count for the key in the keyedPool. At 118 * most one idle object will be added per period. If there is an exception 119 * when calling {@link KeyedObjectPool#addObject(Object)} then no more 120 * checks for that key will be performed. 121 * 122 * @param keyedPool 123 * the keyedPool to check periodically. 124 * @param key 125 * the key to check the idle count of. 126 * @param minIdle 127 * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than 128 * this then add an idle object. 129 * @param period 130 * the frequency to check the number of idle objects in a 131 * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. 132 * @param <K> the type of the pool key 133 * @param <V> the type of pool entries 134 * @return the {@link TimerTask} that will periodically check the pools idle 135 * object count. 136 * @throws IllegalArgumentException 137 * when <code>keyedPool</code>, <code>key</code> is 138 * <code>null</code> or when <code>minIdle</code> is negative or 139 * when <code>period</code> isn't valid for 140 * {@link Timer#schedule(TimerTask, long, long)}. 141 */ 142 public static <K, V> TimerTask checkMinIdle( 143 final KeyedObjectPool<K, V> keyedPool, final K key, 144 final int minIdle, final long period) 145 throws IllegalArgumentException { 146 if (keyedPool == null) { 147 throw new IllegalArgumentException("keyedPool must not be null."); 148 } 149 if (key == null) { 150 throw new IllegalArgumentException("key must not be null."); 151 } 152 if (minIdle < 0) { 153 throw new IllegalArgumentException("minIdle must be non-negative."); 154 } 155 final TimerTask task = new KeyedObjectPoolMinIdleTimerTask<K, V>( 156 keyedPool, key, minIdle); 157 getMinIdleTimer().schedule(task, 0L, period); 158 return task; 159 } 160 161 /** 162 * Periodically check the idle object count for each key in the 163 * <code>Collection</code> <code>keys</code> in the keyedPool. At most one 164 * idle object will be added per period. 165 * 166 * @param keyedPool 167 * the keyedPool to check periodically. 168 * @param keys 169 * a collection of keys to check the idle object count. 170 * @param minIdle 171 * if the {@link KeyedObjectPool#getNumIdle(Object)} is less than 172 * this then add an idle object. 173 * @param period 174 * the frequency to check the number of idle objects in a 175 * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. 176 * @param <K> the type of the pool key 177 * @param <V> the type of pool entries 178 * @return a {@link Map} of key and {@link TimerTask} pairs that will 179 * periodically check the pools idle object count. 180 * @throws IllegalArgumentException 181 * when <code>keyedPool</code>, <code>keys</code>, or any of the 182 * values in the collection is <code>null</code> or when 183 * <code>minIdle</code> is negative or when <code>period</code> 184 * isn't valid for {@link Timer#schedule(TimerTask, long, long)} 185 * . 186 * @see #checkMinIdle(KeyedObjectPool, Object, int, long) 187 */ 188 public static <K, V> Map<K, TimerTask> checkMinIdle( 189 final KeyedObjectPool<K, V> keyedPool, final Collection<K> keys, 190 final int minIdle, final long period) 191 throws IllegalArgumentException { 192 if (keys == null) { 193 throw new IllegalArgumentException("keys must not be null."); 194 } 195 final Map<K, TimerTask> tasks = new HashMap<K, TimerTask>(keys.size()); 196 final Iterator<K> iter = keys.iterator(); 197 while (iter.hasNext()) { 198 final K key = iter.next(); 199 final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period); 200 tasks.put(key, task); 201 } 202 return tasks; 203 } 204 205 /** 206 * Call <code>addObject()</code> on <code>pool</code> <code>count</code> 207 * number of times. 208 * 209 * @param pool 210 * the pool to prefill. 211 * @param count 212 * the number of idle objects to add. 213 * @param <T> the type of objects in the pool 214 * @throws Exception 215 * when {@link ObjectPool#addObject()} fails. 216 * @throws IllegalArgumentException 217 * when <code>pool</code> is <code>null</code>. 218 */ 219 public static <T> void prefill(final ObjectPool<T> pool, final int count) 220 throws Exception, IllegalArgumentException { 221 if (pool == null) { 222 throw new IllegalArgumentException("pool must not be null."); 223 } 224 for (int i = 0; i < count; i++) { 225 pool.addObject(); 226 } 227 } 228 229 /** 230 * Call <code>addObject(Object)</code> on <code>keyedPool</code> with 231 * <code>key</code> <code>count</code> number of times. 232 * 233 * @param keyedPool 234 * the keyedPool to prefill. 235 * @param key 236 * the key to add objects for. 237 * @param count 238 * the number of idle objects to add for <code>key</code>. 239 * @param <K> the type of the pool key 240 * @param <V> the type of pool entries 241 * @throws Exception 242 * when {@link KeyedObjectPool#addObject(Object)} fails. 243 * @throws IllegalArgumentException 244 * when <code>keyedPool</code> or <code>key</code> is 245 * <code>null</code>. 246 */ 247 public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, 248 final K key, final int count) throws Exception, 249 IllegalArgumentException { 250 if (keyedPool == null) { 251 throw new IllegalArgumentException("keyedPool must not be null."); 252 } 253 if (key == null) { 254 throw new IllegalArgumentException("key must not be null."); 255 } 256 for (int i = 0; i < count; i++) { 257 keyedPool.addObject(key); 258 } 259 } 260 261 /** 262 * Call <code>addObject(Object)</code> on <code>keyedPool</code> with each 263 * key in <code>keys</code> for <code>count</code> number of times. This has 264 * the same effect as calling {@link #prefill(KeyedObjectPool, Object, int)} 265 * for each key in the <code>keys</code> collection. 266 * 267 * @param keyedPool 268 * the keyedPool to prefill. 269 * @param keys 270 * {@link Collection} of keys to add objects for. 271 * @param count 272 * the number of idle objects to add for each <code>key</code>. 273 * @param <K> the type of the pool key 274 * @param <V> the type of pool entries 275 * @throws Exception 276 * when {@link KeyedObjectPool#addObject(Object)} fails. 277 * @throws IllegalArgumentException 278 * when <code>keyedPool</code>, <code>keys</code>, or any value 279 * in <code>keys</code> is <code>null</code>. 280 * @see #prefill(KeyedObjectPool, Object, int) 281 */ 282 public static <K, V> void prefill(final KeyedObjectPool<K, V> keyedPool, 283 final Collection<K> keys, final int count) throws Exception, 284 IllegalArgumentException { 285 if (keys == null) { 286 throw new IllegalArgumentException("keys must not be null."); 287 } 288 final Iterator<K> iter = keys.iterator(); 289 while (iter.hasNext()) { 290 prefill(keyedPool, iter.next(), count); 291 } 292 } 293 294 /** 295 * Returns a synchronized (thread-safe) ObjectPool backed by the specified 296 * ObjectPool. 297 * <p> 298 * <b>Note:</b> This should not be used on pool implementations that already 299 * provide proper synchronization such as the pools provided in the Commons 300 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 301 * objects to be returned before allowing another one to be borrowed with 302 * another layer of synchronization will cause liveliness issues or a 303 * deadlock. 304 * </p> 305 * 306 * @param pool 307 * the ObjectPool to be "wrapped" in a synchronized ObjectPool. 308 * @param <T> the type of objects in the pool 309 * @return a synchronized view of the specified ObjectPool. 310 */ 311 public static <T> ObjectPool<T> synchronizedPool(final ObjectPool<T> pool) { 312 if (pool == null) { 313 throw new IllegalArgumentException("pool must not be null."); 314 } 315 /* 316 * assert !(pool instanceof GenericObjectPool) : 317 * "GenericObjectPool is already thread-safe"; assert !(pool instanceof 318 * SoftReferenceObjectPool) : 319 * "SoftReferenceObjectPool is already thread-safe"; assert !(pool 320 * instanceof StackObjectPool) : 321 * "StackObjectPool is already thread-safe"; assert 322 * !"org.apache.commons.pool.composite.CompositeObjectPool" 323 * .equals(pool.getClass().getName()) : 324 * "CompositeObjectPools are already thread-safe"; 325 */ 326 return new SynchronizedObjectPool<T>(pool); 327 } 328 329 /** 330 * Returns a synchronized (thread-safe) KeyedObjectPool backed by the 331 * specified KeyedObjectPool. 332 * <p> 333 * <b>Note:</b> This should not be used on pool implementations that already 334 * provide proper synchronization such as the pools provided in the Commons 335 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 336 * objects to be returned before allowing another one to be borrowed with 337 * another layer of synchronization will cause liveliness issues or a 338 * deadlock. 339 * </p> 340 * 341 * @param keyedPool 342 * the KeyedObjectPool to be "wrapped" in a synchronized 343 * KeyedObjectPool. 344 * @param <K> the type of the pool key 345 * @param <V> the type of pool entries 346 * @return a synchronized view of the specified KeyedObjectPool. 347 */ 348 public static <K, V> KeyedObjectPool<K, V> synchronizedPool( 349 final KeyedObjectPool<K, V> keyedPool) { 350 /* 351 * assert !(keyedPool instanceof GenericKeyedObjectPool) : 352 * "GenericKeyedObjectPool is already thread-safe"; assert !(keyedPool 353 * instanceof StackKeyedObjectPool) : 354 * "StackKeyedObjectPool is already thread-safe"; assert 355 * !"org.apache.commons.pool.composite.CompositeKeyedObjectPool" 356 * .equals(keyedPool.getClass().getName()) : 357 * "CompositeKeyedObjectPools are already thread-safe"; 358 */ 359 return new SynchronizedKeyedObjectPool<K, V>(keyedPool); 360 } 361 362 /** 363 * Returns a synchronized (thread-safe) PooledObjectFactory backed by the 364 * specified PooledObjectFactory. 365 * 366 * @param factory 367 * the PooledObjectFactory to be "wrapped" in a synchronized 368 * PooledObjectFactory. 369 * @param <T> the type of objects in the pool 370 * @return a synchronized view of the specified PooledObjectFactory. 371 */ 372 public static <T> PooledObjectFactory<T> synchronizedPooledFactory( 373 final PooledObjectFactory<T> factory) { 374 return new SynchronizedPooledObjectFactory<T>(factory); 375 } 376 377 /** 378 * Returns a synchronized (thread-safe) KeyedPooledObjectFactory backed by 379 * the specified KeyedPoolableObjectFactory. 380 * 381 * @param keyedFactory 382 * the KeyedPooledObjectFactory to be "wrapped" in a 383 * synchronized KeyedPooledObjectFactory. 384 * @param <K> the type of the pool key 385 * @param <V> the type of pool entries 386 * @return a synchronized view of the specified KeyedPooledObjectFactory. 387 */ 388 public static <K, V> KeyedPooledObjectFactory<K, V> synchronizedKeyedPooledFactory( 389 final KeyedPooledObjectFactory<K, V> keyedFactory) { 390 return new SynchronizedKeyedPooledObjectFactory<K, V>(keyedFactory); 391 } 392 393 /** 394 * Returns a pool that adaptively decreases its size when idle objects are 395 * no longer needed. This is intended as an always thread-safe alternative 396 * to using an idle object evictor provided by many pool implementations. 397 * This is also an effective way to shrink FIFO ordered pools that 398 * experience load spikes. 399 * 400 * @param pool 401 * the ObjectPool to be decorated so it shrinks its idle count 402 * when possible. 403 * @param <T> the type of objects in the pool 404 * @return a pool that adaptively decreases its size when idle objects are 405 * no longer needed. 406 * @see #erodingPool(ObjectPool, float) 407 */ 408 public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool) { 409 return erodingPool(pool, 1f); 410 } 411 412 /** 413 * Returns a pool that adaptively decreases its size when idle objects are 414 * no longer needed. This is intended as an always thread-safe alternative 415 * to using an idle object evictor provided by many pool implementations. 416 * This is also an effective way to shrink FIFO ordered pools that 417 * experience load spikes. 418 * <p> 419 * The factor parameter provides a mechanism to tweak the rate at which the 420 * pool tries to shrink its size. Values between 0 and 1 cause the pool to 421 * try to shrink its size more often. Values greater than 1 cause the pool 422 * to less frequently try to shrink its size. 423 * </p> 424 * 425 * @param pool 426 * the ObjectPool to be decorated so it shrinks its idle count 427 * when possible. 428 * @param factor 429 * a positive value to scale the rate at which the pool tries to 430 * reduce its size. If 0 < factor < 1 then the pool 431 * shrinks more aggressively. If 1 < factor then the pool 432 * shrinks less aggressively. 433 * @param <T> the type of objects in the pool 434 * @return a pool that adaptively decreases its size when idle objects are 435 * no longer needed. 436 * @see #erodingPool(ObjectPool) 437 */ 438 public static <T> ObjectPool<T> erodingPool(final ObjectPool<T> pool, 439 final float factor) { 440 if (pool == null) { 441 throw new IllegalArgumentException("pool must not be null."); 442 } 443 if (factor <= 0f) { 444 throw new IllegalArgumentException("factor must be positive."); 445 } 446 return new ErodingObjectPool<T>(pool, factor); 447 } 448 449 /** 450 * Returns a pool that adaptively decreases its size when idle objects are 451 * no longer needed. This is intended as an always thread-safe alternative 452 * to using an idle object evictor provided by many pool implementations. 453 * This is also an effective way to shrink FIFO ordered pools that 454 * experience load spikes. 455 * 456 * @param keyedPool 457 * the KeyedObjectPool to be decorated so it shrinks its idle 458 * count when possible. 459 * @param <K> the type of the pool key 460 * @param <V> the type of pool entries 461 * @return a pool that adaptively decreases its size when idle objects are 462 * no longer needed. 463 * @see #erodingPool(KeyedObjectPool, float) 464 * @see #erodingPool(KeyedObjectPool, float, boolean) 465 */ 466 public static <K, V> KeyedObjectPool<K, V> erodingPool( 467 final KeyedObjectPool<K, V> keyedPool) { 468 return erodingPool(keyedPool, 1f); 469 } 470 471 /** 472 * Returns a pool that adaptively decreases its size when idle objects are 473 * no longer needed. This is intended as an always thread-safe alternative 474 * to using an idle object evictor provided by many pool implementations. 475 * This is also an effective way to shrink FIFO ordered pools that 476 * experience load spikes. 477 * <p> 478 * The factor parameter provides a mechanism to tweak the rate at which the 479 * pool tries to shrink its size. Values between 0 and 1 cause the pool to 480 * try to shrink its size more often. Values greater than 1 cause the pool 481 * to less frequently try to shrink its size. 482 * </p> 483 * 484 * @param keyedPool 485 * the KeyedObjectPool to be decorated so it shrinks its idle 486 * count when possible. 487 * @param factor 488 * a positive value to scale the rate at which the pool tries to 489 * reduce its size. If 0 < factor < 1 then the pool 490 * shrinks more aggressively. If 1 < factor then the pool 491 * shrinks less aggressively. 492 * @param <K> the type of the pool key 493 * @param <V> the type of pool entries 494 * @return a pool that adaptively decreases its size when idle objects are 495 * no longer needed. 496 * @see #erodingPool(KeyedObjectPool, float, boolean) 497 */ 498 public static <K, V> KeyedObjectPool<K, V> erodingPool( 499 final KeyedObjectPool<K, V> keyedPool, final float factor) { 500 return erodingPool(keyedPool, factor, false); 501 } 502 503 /** 504 * Returns a pool that adaptively decreases its size when idle objects are 505 * no longer needed. This is intended as an always thread-safe alternative 506 * to using an idle object evictor provided by many pool implementations. 507 * This is also an effective way to shrink FIFO ordered pools that 508 * experience load spikes. 509 * <p> 510 * The factor parameter provides a mechanism to tweak the rate at which the 511 * pool tries to shrink its size. Values between 0 and 1 cause the pool to 512 * try to shrink its size more often. Values greater than 1 cause the pool 513 * to less frequently try to shrink its size. 514 * </p> 515 * <p> 516 * The perKey parameter determines if the pool shrinks on a whole pool basis 517 * or a per key basis. When perKey is false, the keys do not have an effect 518 * on the rate at which the pool tries to shrink its size. When perKey is 519 * true, each key is shrunk independently. 520 * </p> 521 * 522 * @param keyedPool 523 * the KeyedObjectPool to be decorated so it shrinks its idle 524 * count when possible. 525 * @param factor 526 * a positive value to scale the rate at which the pool tries to 527 * reduce its size. If 0 < factor < 1 then the pool 528 * shrinks more aggressively. If 1 < factor then the pool 529 * shrinks less aggressively. 530 * @param perKey 531 * when true, each key is treated independently. 532 * @param <K> the type of the pool key 533 * @param <V> the type of pool entries 534 * @return a pool that adaptively decreases its size when idle objects are 535 * no longer needed. 536 * @see #erodingPool(KeyedObjectPool) 537 * @see #erodingPool(KeyedObjectPool, float) 538 */ 539 public static <K, V> KeyedObjectPool<K, V> erodingPool( 540 final KeyedObjectPool<K, V> keyedPool, final float factor, 541 final boolean perKey) { 542 if (keyedPool == null) { 543 throw new IllegalArgumentException("keyedPool must not be null."); 544 } 545 if (factor <= 0f) { 546 throw new IllegalArgumentException("factor must be positive."); 547 } 548 if (perKey) { 549 return new ErodingPerKeyKeyedObjectPool<K, V>(keyedPool, factor); 550 } 551 return new ErodingKeyedObjectPool<K, V>(keyedPool, factor); 552 } 553 554 /** 555 * Get the <code>Timer</code> for checking keyedPool's idle count. 556 * 557 * @return the {@link Timer} for checking keyedPool's idle count. 558 */ 559 private static Timer getMinIdleTimer() { 560 return TimerHolder.MIN_IDLE_TIMER; 561 } 562 563 /** 564 * Timer task that adds objects to the pool until the number of idle 565 * instances reaches the configured minIdle. Note that this is not the same 566 * as the pool's minIdle setting. 567 */ 568 private static class ObjectPoolMinIdleTimerTask<T> extends TimerTask { 569 570 /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ 571 private final int minIdle; 572 573 /** Object pool */ 574 private final ObjectPool<T> pool; 575 576 /** 577 * Create a new ObjectPoolMinIdleTimerTask for the given pool with the 578 * given minIdle setting. 579 * 580 * @param pool 581 * object pool 582 * @param minIdle 583 * number of idle instances to maintain 584 * @throws IllegalArgumentException 585 * if the pool is null 586 */ 587 ObjectPoolMinIdleTimerTask(final ObjectPool<T> pool, final int minIdle) 588 throws IllegalArgumentException { 589 if (pool == null) { 590 throw new IllegalArgumentException("pool must not be null."); 591 } 592 this.pool = pool; 593 this.minIdle = minIdle; 594 } 595 596 /** 597 * {@inheritDoc} 598 */ 599 @Override 600 public void run() { 601 boolean success = false; 602 try { 603 if (pool.getNumIdle() < minIdle) { 604 pool.addObject(); 605 } 606 success = true; 607 608 } catch (Exception e) { 609 cancel(); 610 } finally { 611 // detect other types of Throwable and cancel this Timer 612 if (!success) { 613 cancel(); 614 } 615 } 616 } 617 618 /** 619 * {@inheritDoc} 620 */ 621 @Override 622 public String toString() { 623 final StringBuilder sb = new StringBuilder(); 624 sb.append("ObjectPoolMinIdleTimerTask"); 625 sb.append("{minIdle=").append(minIdle); 626 sb.append(", pool=").append(pool); 627 sb.append('}'); 628 return sb.toString(); 629 } 630 } 631 632 /** 633 * Timer task that adds objects to the pool until the number of idle 634 * instances for the given key reaches the configured minIdle. Note that 635 * this is not the same as the pool's minIdle setting. 636 */ 637 private static class KeyedObjectPoolMinIdleTimerTask<K, V> extends 638 TimerTask { 639 /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */ 640 private final int minIdle; 641 642 /** Key to ensure minIdle for */ 643 private final K key; 644 645 /** Keyed object pool */ 646 private final KeyedObjectPool<K, V> keyedPool; 647 648 /** 649 * Create a new KeyedObjecPoolMinIdleTimerTask. 650 * 651 * @param keyedPool 652 * keyed object pool 653 * @param key 654 * key to ensure minimum number of idle instances 655 * @param minIdle 656 * minimum number of idle instances 657 * @throws IllegalArgumentException 658 * if the key is null 659 */ 660 KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool<K, V> keyedPool, 661 final K key, final int minIdle) throws IllegalArgumentException { 662 if (keyedPool == null) { 663 throw new IllegalArgumentException( 664 "keyedPool must not be null."); 665 } 666 this.keyedPool = keyedPool; 667 this.key = key; 668 this.minIdle = minIdle; 669 } 670 671 /** 672 * {@inheritDoc} 673 */ 674 @Override 675 public void run() { 676 boolean success = false; 677 try { 678 if (keyedPool.getNumIdle(key) < minIdle) { 679 keyedPool.addObject(key); 680 } 681 success = true; 682 683 } catch (Exception e) { 684 cancel(); 685 686 } finally { 687 // detect other types of Throwable and cancel this Timer 688 if (!success) { 689 cancel(); 690 } 691 } 692 } 693 694 /** 695 * {@inheritDoc} 696 */ 697 @Override 698 public String toString() { 699 final StringBuilder sb = new StringBuilder(); 700 sb.append("KeyedObjectPoolMinIdleTimerTask"); 701 sb.append("{minIdle=").append(minIdle); 702 sb.append(", key=").append(key); 703 sb.append(", keyedPool=").append(keyedPool); 704 sb.append('}'); 705 return sb.toString(); 706 } 707 } 708 709 /** 710 * A synchronized (thread-safe) ObjectPool backed by the specified 711 * ObjectPool. 712 * <p> 713 * <b>Note:</b> This should not be used on pool implementations that already 714 * provide proper synchronization such as the pools provided in the Commons 715 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 716 * objects to be returned before allowing another one to be borrowed with 717 * another layer of synchronization will cause liveliness issues or a 718 * deadlock. 719 * </p> 720 */ 721 private static class SynchronizedObjectPool<T> implements ObjectPool<T> { 722 723 /** 724 * Object whose monitor is used to synchronize methods on the wrapped 725 * pool. 726 */ 727 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 728 729 /** the underlying object pool */ 730 private final ObjectPool<T> pool; 731 732 /** 733 * Create a new SynchronizedObjectPool wrapping the given pool. 734 * 735 * @param pool 736 * the ObjectPool to be "wrapped" in a synchronized 737 * ObjectPool. 738 * @throws IllegalArgumentException 739 * if the pool is null 740 */ 741 SynchronizedObjectPool(final ObjectPool<T> pool) 742 throws IllegalArgumentException { 743 if (pool == null) { 744 throw new IllegalArgumentException("pool must not be null."); 745 } 746 this.pool = pool; 747 } 748 749 /** 750 * {@inheritDoc} 751 */ 752 @Override 753 public T borrowObject() throws Exception, NoSuchElementException, 754 IllegalStateException { 755 WriteLock writeLock = readWriteLock.writeLock(); 756 writeLock.lock(); 757 try { 758 return pool.borrowObject(); 759 } finally { 760 writeLock.unlock(); 761 } 762 } 763 764 /** 765 * {@inheritDoc} 766 */ 767 @Override 768 public void returnObject(final T obj) { 769 WriteLock writeLock = readWriteLock.writeLock(); 770 writeLock.lock(); 771 try { 772 pool.returnObject(obj); 773 } catch (Exception e) { 774 // swallowed as of Pool 2 775 } finally { 776 writeLock.unlock(); 777 } 778 } 779 780 /** 781 * {@inheritDoc} 782 */ 783 @Override 784 public void invalidateObject(final T obj) { 785 WriteLock writeLock = readWriteLock.writeLock(); 786 writeLock.lock(); 787 try { 788 pool.invalidateObject(obj); 789 } catch (Exception e) { 790 // swallowed as of Pool 2 791 } finally { 792 writeLock.unlock(); 793 } 794 } 795 796 /** 797 * {@inheritDoc} 798 */ 799 @Override 800 public void addObject() throws Exception, IllegalStateException, 801 UnsupportedOperationException { 802 WriteLock writeLock = readWriteLock.writeLock(); 803 writeLock.lock(); 804 try { 805 pool.addObject(); 806 } finally { 807 writeLock.unlock(); 808 } 809 } 810 811 /** 812 * {@inheritDoc} 813 */ 814 @Override 815 public int getNumIdle() { 816 ReadLock readLock = readWriteLock.readLock(); 817 readLock.lock(); 818 try { 819 return pool.getNumIdle(); 820 } finally { 821 readLock.unlock(); 822 } 823 } 824 825 /** 826 * {@inheritDoc} 827 */ 828 @Override 829 public int getNumActive() { 830 ReadLock readLock = readWriteLock.readLock(); 831 readLock.lock(); 832 try { 833 return pool.getNumActive(); 834 } finally { 835 readLock.unlock(); 836 } 837 } 838 839 /** 840 * {@inheritDoc} 841 */ 842 @Override 843 public void clear() throws Exception, UnsupportedOperationException { 844 WriteLock writeLock = readWriteLock.writeLock(); 845 writeLock.lock(); 846 try { 847 pool.clear(); 848 } finally { 849 writeLock.unlock(); 850 } 851 } 852 853 /** 854 * {@inheritDoc} 855 */ 856 @Override 857 public void close() { 858 WriteLock writeLock = readWriteLock.writeLock(); 859 writeLock.lock(); 860 try { 861 pool.close(); 862 } catch (Exception e) { 863 // swallowed as of Pool 2 864 } finally { 865 writeLock.unlock(); 866 } 867 } 868 869 /** 870 * {@inheritDoc} 871 */ 872 @Override 873 public String toString() { 874 final StringBuilder sb = new StringBuilder(); 875 sb.append("SynchronizedObjectPool"); 876 sb.append("{pool=").append(pool); 877 sb.append('}'); 878 return sb.toString(); 879 } 880 } 881 882 /** 883 * A synchronized (thread-safe) KeyedObjectPool backed by the specified 884 * KeyedObjectPool. 885 * <p> 886 * <b>Note:</b> This should not be used on pool implementations that already 887 * provide proper synchronization such as the pools provided in the Commons 888 * Pool library. Wrapping a pool that {@link #wait() waits} for poolable 889 * objects to be returned before allowing another one to be borrowed with 890 * another layer of synchronization will cause liveliness issues or a 891 * deadlock. 892 * </p> 893 */ 894 private static class SynchronizedKeyedObjectPool<K, V> implements 895 KeyedObjectPool<K, V> { 896 897 /** 898 * Object whose monitor is used to synchronize methods on the wrapped 899 * pool. 900 */ 901 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 902 903 /** Underlying object pool */ 904 private final KeyedObjectPool<K, V> keyedPool; 905 906 /** 907 * Create a new SynchronizedKeyedObjectPool wrapping the given pool 908 * 909 * @param keyedPool 910 * KeyedObjectPool to wrap 911 * @throws IllegalArgumentException 912 * if keyedPool is null 913 */ 914 SynchronizedKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool) 915 throws IllegalArgumentException { 916 if (keyedPool == null) { 917 throw new IllegalArgumentException( 918 "keyedPool must not be null."); 919 } 920 this.keyedPool = keyedPool; 921 } 922 923 /** 924 * {@inheritDoc} 925 */ 926 @Override 927 public V borrowObject(final K key) throws Exception, 928 NoSuchElementException, IllegalStateException { 929 WriteLock writeLock = readWriteLock.writeLock(); 930 writeLock.lock(); 931 try { 932 return keyedPool.borrowObject(key); 933 } finally { 934 writeLock.unlock(); 935 } 936 } 937 938 /** 939 * {@inheritDoc} 940 */ 941 @Override 942 public void returnObject(final K key, final V obj) { 943 WriteLock writeLock = readWriteLock.writeLock(); 944 writeLock.lock(); 945 try { 946 keyedPool.returnObject(key, obj); 947 } catch (Exception e) { 948 // swallowed 949 } finally { 950 writeLock.unlock(); 951 } 952 } 953 954 /** 955 * {@inheritDoc} 956 */ 957 @Override 958 public void invalidateObject(final K key, final V obj) { 959 WriteLock writeLock = readWriteLock.writeLock(); 960 writeLock.lock(); 961 try { 962 keyedPool.invalidateObject(key, obj); 963 } catch (Exception e) { 964 // swallowed as of Pool 2 965 } finally { 966 writeLock.unlock(); 967 } 968 } 969 970 /** 971 * {@inheritDoc} 972 */ 973 @Override 974 public void addObject(final K key) throws Exception, 975 IllegalStateException, UnsupportedOperationException { 976 WriteLock writeLock = readWriteLock.writeLock(); 977 writeLock.lock(); 978 try { 979 keyedPool.addObject(key); 980 } finally { 981 writeLock.unlock(); 982 } 983 } 984 985 /** 986 * {@inheritDoc} 987 */ 988 @Override 989 public int getNumIdle(final K key) { 990 ReadLock readLock = readWriteLock.readLock(); 991 readLock.lock(); 992 try { 993 return keyedPool.getNumIdle(key); 994 } finally { 995 readLock.unlock(); 996 } 997 } 998 999 /** 1000 * {@inheritDoc} 1001 */ 1002 @Override 1003 public int getNumActive(final K key) { 1004 ReadLock readLock = readWriteLock.readLock(); 1005 readLock.lock(); 1006 try { 1007 return keyedPool.getNumActive(key); 1008 } finally { 1009 readLock.unlock(); 1010 } 1011 } 1012 1013 /** 1014 * {@inheritDoc} 1015 */ 1016 @Override 1017 public int getNumIdle() { 1018 ReadLock readLock = readWriteLock.readLock(); 1019 readLock.lock(); 1020 try { 1021 return keyedPool.getNumIdle(); 1022 } finally { 1023 readLock.unlock(); 1024 } 1025 } 1026 1027 /** 1028 * {@inheritDoc} 1029 */ 1030 @Override 1031 public int getNumActive() { 1032 ReadLock readLock = readWriteLock.readLock(); 1033 readLock.lock(); 1034 try { 1035 return keyedPool.getNumActive(); 1036 } finally { 1037 readLock.unlock(); 1038 } 1039 } 1040 1041 /** 1042 * {@inheritDoc} 1043 */ 1044 @Override 1045 public void clear() throws Exception, UnsupportedOperationException { 1046 WriteLock writeLock = readWriteLock.writeLock(); 1047 writeLock.lock(); 1048 try { 1049 keyedPool.clear(); 1050 } finally { 1051 writeLock.unlock(); 1052 } 1053 } 1054 1055 /** 1056 * {@inheritDoc} 1057 */ 1058 @Override 1059 public void clear(final K key) throws Exception, 1060 UnsupportedOperationException { 1061 WriteLock writeLock = readWriteLock.writeLock(); 1062 writeLock.lock(); 1063 try { 1064 keyedPool.clear(key); 1065 } finally { 1066 writeLock.unlock(); 1067 } 1068 } 1069 1070 /** 1071 * {@inheritDoc} 1072 */ 1073 @Override 1074 public void close() { 1075 WriteLock writeLock = readWriteLock.writeLock(); 1076 writeLock.lock(); 1077 try { 1078 keyedPool.close(); 1079 } catch (Exception e) { 1080 // swallowed as of Pool 2 1081 } finally { 1082 writeLock.unlock(); 1083 } 1084 } 1085 1086 /** 1087 * {@inheritDoc} 1088 */ 1089 @Override 1090 public String toString() { 1091 final StringBuilder sb = new StringBuilder(); 1092 sb.append("SynchronizedKeyedObjectPool"); 1093 sb.append("{keyedPool=").append(keyedPool); 1094 sb.append('}'); 1095 return sb.toString(); 1096 } 1097 } 1098 1099 /** 1100 * A fully synchronized PooledObjectFactory that wraps a 1101 * PooledObjectFactory and synchronizes access to the wrapped factory 1102 * methods. 1103 * <p> 1104 * <b>Note:</b> This should not be used on pool implementations that already 1105 * provide proper synchronization such as the pools provided in the Commons 1106 * Pool library. 1107 * </p> 1108 */ 1109 private static class SynchronizedPooledObjectFactory<T> implements 1110 PooledObjectFactory<T> { 1111 /** Synchronization lock */ 1112 private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); 1113 1114 /** Wrapped factory */ 1115 private final PooledObjectFactory<T> factory; 1116 1117 /** 1118 * Create a SynchronizedPoolableObjectFactory wrapping the given 1119 * factory. 1120 * 1121 * @param factory 1122 * underlying factory to wrap 1123 * @throws IllegalArgumentException 1124 * if the factory is null 1125 */ 1126 SynchronizedPooledObjectFactory(final PooledObjectFactory<T> factory) 1127 throws IllegalArgumentException { 1128 if (factory == null) { 1129 throw new IllegalArgumentException("factory must not be null."); 1130 } 1131 this.factory = factory; 1132 } 1133 1134 /** 1135 * {@inheritDoc} 1136 */ 1137 @Override 1138 public PooledObject<T> makeObject() throws Exception { 1139 writeLock.lock(); 1140 try { 1141 return factory.makeObject(); 1142 } finally { 1143 writeLock.unlock(); 1144 } 1145 } 1146 1147 /** 1148 * {@inheritDoc} 1149 */ 1150 @Override 1151 public void destroyObject(final PooledObject<T> p) throws Exception { 1152 writeLock.lock(); 1153 try { 1154 factory.destroyObject(p); 1155 } finally { 1156 writeLock.unlock(); 1157 } 1158 } 1159 1160 /** 1161 * {@inheritDoc} 1162 */ 1163 @Override 1164 public boolean validateObject(final PooledObject<T> p) { 1165 writeLock.lock(); 1166 try { 1167 return factory.validateObject(p); 1168 } finally { 1169 writeLock.unlock(); 1170 } 1171 } 1172 1173 /** 1174 * {@inheritDoc} 1175 */ 1176 @Override 1177 public void activateObject(final PooledObject<T> p) throws Exception { 1178 writeLock.lock(); 1179 try { 1180 factory.activateObject(p); 1181 } finally { 1182 writeLock.unlock(); 1183 } 1184 } 1185 1186 /** 1187 * {@inheritDoc} 1188 */ 1189 @Override 1190 public void passivateObject(final PooledObject<T> p) throws Exception { 1191 writeLock.lock(); 1192 try { 1193 factory.passivateObject(p); 1194 } finally { 1195 writeLock.unlock(); 1196 } 1197 } 1198 1199 /** 1200 * {@inheritDoc} 1201 */ 1202 @Override 1203 public String toString() { 1204 final StringBuilder sb = new StringBuilder(); 1205 sb.append("SynchronizedPoolableObjectFactory"); 1206 sb.append("{factory=").append(factory); 1207 sb.append('}'); 1208 return sb.toString(); 1209 } 1210 } 1211 1212 /** 1213 * A fully synchronized KeyedPooledObjectFactory that wraps a 1214 * KeyedPooledObjectFactory and synchronizes access to the wrapped factory 1215 * methods. 1216 * <p> 1217 * <b>Note:</b> This should not be used on pool implementations that already 1218 * provide proper synchronization such as the pools provided in the Commons 1219 * Pool library. 1220 * </p> 1221 */ 1222 private static class SynchronizedKeyedPooledObjectFactory<K, V> 1223 implements KeyedPooledObjectFactory<K, V> { 1224 /** Synchronization lock */ 1225 private final WriteLock writeLock = new ReentrantReadWriteLock().writeLock(); 1226 1227 /** Wrapped factory */ 1228 private final KeyedPooledObjectFactory<K, V> keyedFactory; 1229 1230 /** 1231 * Create a SynchronizedKeyedPoolableObjectFactory wrapping the given 1232 * factory. 1233 * 1234 * @param keyedFactory 1235 * underlying factory to wrap 1236 * @throws IllegalArgumentException 1237 * if the factory is null 1238 */ 1239 SynchronizedKeyedPooledObjectFactory( 1240 final KeyedPooledObjectFactory<K, V> keyedFactory) 1241 throws IllegalArgumentException { 1242 if (keyedFactory == null) { 1243 throw new IllegalArgumentException( 1244 "keyedFactory must not be null."); 1245 } 1246 this.keyedFactory = keyedFactory; 1247 } 1248 1249 /** 1250 * {@inheritDoc} 1251 */ 1252 @Override 1253 public PooledObject<V> makeObject(final K key) throws Exception { 1254 writeLock.lock(); 1255 try { 1256 return keyedFactory.makeObject(key); 1257 } finally { 1258 writeLock.unlock(); 1259 } 1260 } 1261 1262 /** 1263 * {@inheritDoc} 1264 */ 1265 @Override 1266 public void destroyObject(final K key, final PooledObject<V> p) throws Exception { 1267 writeLock.lock(); 1268 try { 1269 keyedFactory.destroyObject(key, p); 1270 } finally { 1271 writeLock.unlock(); 1272 } 1273 } 1274 1275 /** 1276 * {@inheritDoc} 1277 */ 1278 @Override 1279 public boolean validateObject(final K key, final PooledObject<V> p) { 1280 writeLock.lock(); 1281 try { 1282 return keyedFactory.validateObject(key, p); 1283 } finally { 1284 writeLock.unlock(); 1285 } 1286 } 1287 1288 /** 1289 * {@inheritDoc} 1290 */ 1291 @Override 1292 public void activateObject(final K key, final PooledObject<V> p) throws Exception { 1293 writeLock.lock(); 1294 try { 1295 keyedFactory.activateObject(key, p); 1296 } finally { 1297 writeLock.unlock(); 1298 } 1299 } 1300 1301 /** 1302 * {@inheritDoc} 1303 */ 1304 @Override 1305 public void passivateObject(final K key, final PooledObject<V> p) throws Exception { 1306 writeLock.lock(); 1307 try { 1308 keyedFactory.passivateObject(key, p); 1309 } finally { 1310 writeLock.unlock(); 1311 } 1312 } 1313 1314 /** 1315 * {@inheritDoc} 1316 */ 1317 @Override 1318 public String toString() { 1319 final StringBuilder sb = new StringBuilder(); 1320 sb.append("SynchronizedKeyedPoolableObjectFactory"); 1321 sb.append("{keyedFactory=").append(keyedFactory); 1322 sb.append('}'); 1323 return sb.toString(); 1324 } 1325 } 1326 1327 /** 1328 * Encapsulate the logic for when the next poolable object should be 1329 * discarded. Each time update is called, the next time to shrink is 1330 * recomputed, based on the float factor, number of idle instances in the 1331 * pool and high water mark. Float factor is assumed to be between 0 and 1. 1332 * Values closer to 1 cause less frequent erosion events. Erosion event 1333 * timing also depends on numIdle. When this value is relatively high (close 1334 * to previously established high water mark), erosion occurs more 1335 * frequently. 1336 */ 1337 private static class ErodingFactor { 1338 /** Determines frequency of "erosion" events */ 1339 private final float factor; 1340 1341 /** Time of next shrink event */ 1342 private transient volatile long nextShrink; 1343 1344 /** High water mark - largest numIdle encountered */ 1345 private transient volatile int idleHighWaterMark; 1346 1347 /** 1348 * Create a new ErodingFactor with the given erosion factor. 1349 * 1350 * @param factor 1351 * erosion factor 1352 */ 1353 public ErodingFactor(final float factor) { 1354 this.factor = factor; 1355 nextShrink = System.currentTimeMillis() + (long) (900000 * factor); // now 1356 // + 1357 // 15 1358 // min 1359 // * 1360 // factor 1361 idleHighWaterMark = 1; 1362 } 1363 1364 /** 1365 * Updates internal state using the supplied time and numIdle. 1366 * 1367 * @param now 1368 * current time 1369 * @param numIdle 1370 * number of idle elements in the pool 1371 */ 1372 public void update(final long now, final int numIdle) { 1373 final int idle = Math.max(0, numIdle); 1374 idleHighWaterMark = Math.max(idle, idleHighWaterMark); 1375 final float maxInterval = 15f; 1376 final float minutes = maxInterval + 1377 ((1f - maxInterval) / idleHighWaterMark) * idle; 1378 nextShrink = now + (long) (minutes * 60000f * factor); 1379 } 1380 1381 /** 1382 * Returns the time of the next erosion event. 1383 * 1384 * @return next shrink time 1385 */ 1386 public long getNextShrink() { 1387 return nextShrink; 1388 } 1389 1390 /** 1391 * {@inheritDoc} 1392 */ 1393 @Override 1394 public String toString() { 1395 return "ErodingFactor{" + "factor=" + factor + 1396 ", idleHighWaterMark=" + idleHighWaterMark + '}'; 1397 } 1398 } 1399 1400 /** 1401 * Decorates an object pool, adding "eroding" behavior. Based on the 1402 * configured {@link #factor erosion factor}, objects returning to the pool 1403 * may be invalidated instead of being added to idle capacity. 1404 */ 1405 private static class ErodingObjectPool<T> implements ObjectPool<T> { 1406 /** Underlying object pool */ 1407 private final ObjectPool<T> pool; 1408 1409 /** Erosion factor */ 1410 private final ErodingFactor factor; 1411 1412 /** 1413 * Create an ErodingObjectPool wrapping the given pool using the 1414 * specified erosion factor. 1415 * 1416 * @param pool 1417 * underlying pool 1418 * @param factor 1419 * erosion factor - determines the frequency of erosion 1420 * events 1421 * @see #factor 1422 */ 1423 public ErodingObjectPool(final ObjectPool<T> pool, final float factor) { 1424 this.pool = pool; 1425 this.factor = new ErodingFactor(factor); 1426 } 1427 1428 /** 1429 * {@inheritDoc} 1430 */ 1431 @Override 1432 public T borrowObject() throws Exception, NoSuchElementException, 1433 IllegalStateException { 1434 return pool.borrowObject(); 1435 } 1436 1437 /** 1438 * Returns obj to the pool, unless erosion is triggered, in which case 1439 * obj is invalidated. Erosion is triggered when there are idle 1440 * instances in the pool and more than the {@link #factor erosion 1441 * factor}-determined time has elapsed since the last returnObject 1442 * activation. 1443 * 1444 * @param obj 1445 * object to return or invalidate 1446 * @see #factor 1447 */ 1448 @Override 1449 public void returnObject(final T obj) { 1450 boolean discard = false; 1451 final long now = System.currentTimeMillis(); 1452 synchronized (pool) { 1453 if (factor.getNextShrink() < now) { // XXX: Pool 3: move test 1454 // out of sync block 1455 final int numIdle = pool.getNumIdle(); 1456 if (numIdle > 0) { 1457 discard = true; 1458 } 1459 1460 factor.update(now, numIdle); 1461 } 1462 } 1463 try { 1464 if (discard) { 1465 pool.invalidateObject(obj); 1466 } else { 1467 pool.returnObject(obj); 1468 } 1469 } catch (Exception e) { 1470 // swallowed 1471 } 1472 } 1473 1474 /** 1475 * {@inheritDoc} 1476 */ 1477 @Override 1478 public void invalidateObject(final T obj) { 1479 try { 1480 pool.invalidateObject(obj); 1481 } catch (Exception e) { 1482 // swallowed 1483 } 1484 } 1485 1486 /** 1487 * {@inheritDoc} 1488 */ 1489 @Override 1490 public void addObject() throws Exception, IllegalStateException, 1491 UnsupportedOperationException { 1492 pool.addObject(); 1493 } 1494 1495 /** 1496 * {@inheritDoc} 1497 */ 1498 @Override 1499 public int getNumIdle() { 1500 return pool.getNumIdle(); 1501 } 1502 1503 /** 1504 * {@inheritDoc} 1505 */ 1506 @Override 1507 public int getNumActive() { 1508 return pool.getNumActive(); 1509 } 1510 1511 /** 1512 * {@inheritDoc} 1513 */ 1514 @Override 1515 public void clear() throws Exception, UnsupportedOperationException { 1516 pool.clear(); 1517 } 1518 1519 /** 1520 * {@inheritDoc} 1521 */ 1522 @Override 1523 public void close() { 1524 try { 1525 pool.close(); 1526 } catch (Exception e) { 1527 // swallowed 1528 } 1529 } 1530 1531 /** 1532 * {@inheritDoc} 1533 */ 1534 @Override 1535 public String toString() { 1536 return "ErodingObjectPool{" + "factor=" + factor + ", pool=" + 1537 pool + '}'; 1538 } 1539 } 1540 1541 /** 1542 * Decorates a keyed object pool, adding "eroding" behavior. Based on the 1543 * configured {@link #factor erosion factor}, objects returning to the pool 1544 * may be invalidated instead of being added to idle capacity. 1545 */ 1546 private static class ErodingKeyedObjectPool<K, V> implements 1547 KeyedObjectPool<K, V> { 1548 /** Underlying pool */ 1549 private final KeyedObjectPool<K, V> keyedPool; 1550 1551 /** Erosion factor */ 1552 private final ErodingFactor erodingFactor; 1553 1554 /** 1555 * Create an ErodingObjectPool wrapping the given pool using the 1556 * specified erosion factor. 1557 * 1558 * @param keyedPool 1559 * underlying pool 1560 * @param factor 1561 * erosion factor - determines the frequency of erosion 1562 * events 1563 * @see #erodingFactor 1564 */ 1565 public ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, 1566 final float factor) { 1567 this(keyedPool, new ErodingFactor(factor)); 1568 } 1569 1570 /** 1571 * Create an ErodingObjectPool wrapping the given pool using the 1572 * specified erosion factor. 1573 * 1574 * @param keyedPool 1575 * underlying pool - must not be null 1576 * @param erodingFactor 1577 * erosion factor - determines the frequency of erosion 1578 * events 1579 * @see #factor 1580 */ 1581 protected ErodingKeyedObjectPool(final KeyedObjectPool<K, V> keyedPool, 1582 final ErodingFactor erodingFactor) { 1583 if (keyedPool == null) { 1584 throw new IllegalArgumentException( 1585 "keyedPool must not be null."); 1586 } 1587 this.keyedPool = keyedPool; 1588 this.erodingFactor = erodingFactor; 1589 } 1590 1591 /** 1592 * {@inheritDoc} 1593 */ 1594 @Override 1595 public V borrowObject(final K key) throws Exception, 1596 NoSuchElementException, IllegalStateException { 1597 return keyedPool.borrowObject(key); 1598 } 1599 1600 /** 1601 * Returns obj to the pool, unless erosion is triggered, in which case 1602 * obj is invalidated. Erosion is triggered when there are idle 1603 * instances in the pool associated with the given key and more than the 1604 * configured {@link #erodingFactor erosion factor} time has elapsed 1605 * since the last returnObject activation. 1606 * 1607 * @param obj 1608 * object to return or invalidate 1609 * @param key 1610 * key 1611 * @see #erodingFactor 1612 */ 1613 @Override 1614 public void returnObject(final K key, final V obj) throws Exception { 1615 boolean discard = false; 1616 final long now = System.currentTimeMillis(); 1617 final ErodingFactor factor = getErodingFactor(key); 1618 synchronized (keyedPool) { 1619 if (factor.getNextShrink() < now) { 1620 final int numIdle = getNumIdle(key); 1621 if (numIdle > 0) { 1622 discard = true; 1623 } 1624 1625 factor.update(now, numIdle); 1626 } 1627 } 1628 try { 1629 if (discard) { 1630 keyedPool.invalidateObject(key, obj); 1631 } else { 1632 keyedPool.returnObject(key, obj); 1633 } 1634 } catch (Exception e) { 1635 // swallowed 1636 } 1637 } 1638 1639 /** 1640 * Returns the eroding factor for the given key 1641 * 1642 * @param key 1643 * key 1644 * @return eroding factor for the given keyed pool 1645 */ 1646 protected ErodingFactor getErodingFactor(final K key) { 1647 return erodingFactor; 1648 } 1649 1650 /** 1651 * {@inheritDoc} 1652 */ 1653 @Override 1654 public void invalidateObject(final K key, final V obj) { 1655 try { 1656 keyedPool.invalidateObject(key, obj); 1657 } catch (Exception e) { 1658 // swallowed 1659 } 1660 } 1661 1662 /** 1663 * {@inheritDoc} 1664 */ 1665 @Override 1666 public void addObject(final K key) throws Exception, 1667 IllegalStateException, UnsupportedOperationException { 1668 keyedPool.addObject(key); 1669 } 1670 1671 /** 1672 * {@inheritDoc} 1673 */ 1674 @Override 1675 public int getNumIdle() { 1676 return keyedPool.getNumIdle(); 1677 } 1678 1679 /** 1680 * {@inheritDoc} 1681 */ 1682 @Override 1683 public int getNumIdle(final K key) { 1684 return keyedPool.getNumIdle(key); 1685 } 1686 1687 /** 1688 * {@inheritDoc} 1689 */ 1690 @Override 1691 public int getNumActive() { 1692 return keyedPool.getNumActive(); 1693 } 1694 1695 /** 1696 * {@inheritDoc} 1697 */ 1698 @Override 1699 public int getNumActive(final K key) { 1700 return keyedPool.getNumActive(key); 1701 } 1702 1703 /** 1704 * {@inheritDoc} 1705 */ 1706 @Override 1707 public void clear() throws Exception, UnsupportedOperationException { 1708 keyedPool.clear(); 1709 } 1710 1711 /** 1712 * {@inheritDoc} 1713 */ 1714 @Override 1715 public void clear(final K key) throws Exception, 1716 UnsupportedOperationException { 1717 keyedPool.clear(key); 1718 } 1719 1720 /** 1721 * {@inheritDoc} 1722 */ 1723 @Override 1724 public void close() { 1725 try { 1726 keyedPool.close(); 1727 } catch (Exception e) { 1728 // swallowed 1729 } 1730 } 1731 1732 /** 1733 * Returns the underlying pool 1734 * 1735 * @return the keyed pool that this ErodingKeyedObjectPool wraps 1736 */ 1737 protected KeyedObjectPool<K, V> getKeyedPool() { 1738 return keyedPool; 1739 } 1740 1741 /** 1742 * {@inheritDoc} 1743 */ 1744 @Override 1745 public String toString() { 1746 return "ErodingKeyedObjectPool{" + "factor=" + 1747 erodingFactor + ", keyedPool=" + keyedPool + '}'; 1748 } 1749 } 1750 1751 /** 1752 * Extends ErodingKeyedObjectPool to allow erosion to take place on a 1753 * per-key basis. Timing of erosion events is tracked separately for 1754 * separate keyed pools. 1755 */ 1756 private static class ErodingPerKeyKeyedObjectPool<K, V> extends 1757 ErodingKeyedObjectPool<K, V> { 1758 /** Erosion factor - same for all pools */ 1759 private final float factor; 1760 1761 /** Map of ErodingFactor instances keyed on pool keys */ 1762 private final Map<K, ErodingFactor> factors = Collections.synchronizedMap(new HashMap<K, ErodingFactor>()); 1763 1764 /** 1765 * Create a new ErordingPerKeyKeyedObjectPool decorating the given keyed 1766 * pool with the specified erosion factor. 1767 * 1768 * @param keyedPool 1769 * underlying keyed pool 1770 * @param factor 1771 * erosion factor 1772 */ 1773 public ErodingPerKeyKeyedObjectPool( 1774 final KeyedObjectPool<K, V> keyedPool, final float factor) { 1775 super(keyedPool, null); 1776 this.factor = factor; 1777 } 1778 1779 /** 1780 * {@inheritDoc} 1781 */ 1782 @Override 1783 protected ErodingFactor getErodingFactor(final K key) { 1784 ErodingFactor eFactor = factors.get(key); 1785 // this may result in two ErodingFactors being created for a key 1786 // since they are small and cheap this is okay. 1787 if (eFactor == null) { 1788 eFactor = new ErodingFactor(this.factor); 1789 factors.put(key, eFactor); 1790 } 1791 return eFactor; 1792 } 1793 1794 /** 1795 * {@inheritDoc} 1796 */ 1797 @Override 1798 public String toString() { 1799 return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor + 1800 ", keyedPool=" + getKeyedPool() + '}'; 1801 } 1802 } 1803}