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