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