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