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