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