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: 1086470 $ $Date: 2011-03-28 21:30:47 -0700 (Mon, 28 Mar 2011) $
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; //@GuardedBy("this")
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 * Should the supplied Throwable be re-thrown (eg if it is an instance of
055 * one of the Throwables that should never be swallowed). Used by the pool
056 * error handling for operations that throw exceptions that normally need to
057 * be ignored.
058 * @param t The Throwable to check
059 * @throws ThreadDeath if that is passed in
060 * @throws VirtualMachineError if that is passed in
061 * @since Pool 1.5.5
062 */
063 public static void checkRethrow(Throwable t) {
064 if (t instanceof ThreadDeath) {
065 throw (ThreadDeath) t;
066 }
067 if (t instanceof VirtualMachineError) {
068 throw (VirtualMachineError) t;
069 }
070 // All other instances of Throwable will be silently swallowed
071 }
072
073 /**
074 * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
075 * needed. This method is the equivalent of calling
076 * {@link #adapt(KeyedPoolableObjectFactory, Object) PoolUtils.adapt(aKeyedPoolableObjectFactory, new Object())}.
077 *
078 * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
079 * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with an internal key.
080 * @throws IllegalArgumentException when <code>keyedFactory</code> is <code>null</code>.
081 * @see #adapt(KeyedPoolableObjectFactory, Object)
082 * @since Pool 1.3
083 */
084 public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
085 return adapt(keyedFactory, new Object());
086 }
087
088 /**
089 * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
090 * needed using the specified <code>key</code> when delegating.
091 *
092 * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
093 * @param key the key to use when delegating.
094 * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with the specified key.
095 * @throws IllegalArgumentException when <code>keyedFactory</code> or <code>key</code> is <code>null</code>.
096 * @see #adapt(KeyedPoolableObjectFactory)
097 * @since Pool 1.3
098 */
099 public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory, final Object key) throws IllegalArgumentException {
100 return new PoolableObjectFactoryAdaptor(keyedFactory, key);
101 }
102
103 /**
104 * Adapt a <code>PoolableObjectFactory</code> instance to work where a <code>KeyedPoolableObjectFactory</code> is
105 * needed. The key is ignored.
106 *
107 * @param factory the {@link PoolableObjectFactory} to delegate to.
108 * @return a {@link KeyedPoolableObjectFactory} that delegates to <code>factory</code> ignoring the key.
109 * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
110 * @since Pool 1.3
111 */
112 public static KeyedPoolableObjectFactory adapt(final PoolableObjectFactory factory) throws IllegalArgumentException {
113 return new KeyedPoolableObjectFactoryAdaptor(factory);
114 }
115
116 /**
117 * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed. This is the
118 * equivalent of calling {@link #adapt(KeyedObjectPool, Object) PoolUtils.adapt(aKeyedObjectPool, new Object())}.
119 *
120 * @param keyedPool the {@link KeyedObjectPool} to delegate to.
121 * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with an internal key.
122 * @throws IllegalArgumentException when <code>keyedPool</code> is <code>null</code>.
123 * @see #adapt(KeyedObjectPool, Object)
124 * @since Pool 1.3
125 */
126 public static ObjectPool adapt(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
127 return adapt(keyedPool, new Object());
128 }
129
130 /**
131 * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed using the
132 * specified <code>key</code> when delegating.
133 *
134 * @param keyedPool the {@link KeyedObjectPool} to delegate to.
135 * @param key the key to use when delegating.
136 * @return an {@link ObjectPool} that delegates to <code>keyedPool</code> with the specified key.
137 * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
138 * @see #adapt(KeyedObjectPool)
139 * @since Pool 1.3
140 */
141 public static ObjectPool adapt(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
142 return new ObjectPoolAdaptor(keyedPool, key);
143 }
144
145 /**
146 * Adapt an <code>ObjectPool</code> to work where an <code>KeyedObjectPool</code> is needed.
147 * The key is ignored.
148 *
149 * @param pool the {@link ObjectPool} to delegate to.
150 * @return a {@link KeyedObjectPool} that delegates to <code>pool</code> ignoring the key.
151 * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
152 * @since Pool 1.3
153 */
154 public static KeyedObjectPool adapt(final ObjectPool pool) throws IllegalArgumentException {
155 return new KeyedObjectPoolAdaptor(pool);
156 }
157
158 /**
159 * Wraps an <code>ObjectPool</code> and dynamically checks the type of objects borrowed and returned to the pool.
160 * If an object is passed to the pool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
161 *
162 * @param pool the pool to enforce type safety on
163 * @param type the class type to enforce.
164 * @return an <code>ObjectPool</code> that will only allow objects of <code>type</code>
165 * @since Pool 1.3
166 */
167 public static ObjectPool checkedPool(final ObjectPool pool, final Class type) {
168 if (pool == null) {
169 throw new IllegalArgumentException("pool must not be null.");
170 }
171 if (type == null) {
172 throw new IllegalArgumentException("type must not be null.");
173 }
174 return new CheckedObjectPool(pool, type);
175 }
176
177 /**
178 * Wraps a <code>KeyedObjectPool</code> and dynamically checks the type of objects borrowed and returned to the keyedPool.
179 * If an object is passed to the keyedPool that isn't of type <code>type</code> a {@link ClassCastException} will be thrown.
180 *
181 * @param keyedPool the keyedPool to enforce type safety on
182 * @param type the class type to enforce.
183 * @return a <code>KeyedObjectPool</code> that will only allow objects of <code>type</code>
184 * @since Pool 1.3
185 */
186 public static KeyedObjectPool checkedPool(final KeyedObjectPool keyedPool, final Class type) {
187 if (keyedPool == null) {
188 throw new IllegalArgumentException("keyedPool must not be null.");
189 }
190 if (type == null) {
191 throw new IllegalArgumentException("type must not be null.");
192 }
193 return new CheckedKeyedObjectPool(keyedPool, type);
194 }
195
196 /**
197 * Periodically check the idle object count for the pool. At most one idle object will be added per period.
198 * If there is an exception when calling {@link ObjectPool#addObject()} then no more checks will be performed.
199 *
200 * @param pool the pool to check periodically.
201 * @param minIdle if the {@link ObjectPool#getNumIdle()} is less than this then add an idle object.
202 * @param period the frequency to check the number of idle objects in a pool, see
203 * {@link Timer#schedule(TimerTask, long, long)}.
204 * @return the {@link TimerTask} that will periodically check the pools idle object count.
205 * @throws IllegalArgumentException when <code>pool</code> is <code>null</code> or
206 * when <code>minIdle</code> is negative or when <code>period</code> isn't
207 * valid for {@link Timer#schedule(TimerTask, long, long)}.
208 * @since Pool 1.3
209 */
210 public static TimerTask checkMinIdle(final ObjectPool pool, final int minIdle, final long period) throws IllegalArgumentException {
211 if (pool == null) {
212 throw new IllegalArgumentException("keyedPool must not be null.");
213 }
214 if (minIdle < 0) {
215 throw new IllegalArgumentException("minIdle must be non-negative.");
216 }
217 final TimerTask task = new ObjectPoolMinIdleTimerTask(pool, minIdle);
218 getMinIdleTimer().schedule(task, 0L, period);
219 return task;
220 }
221
222 /**
223 * Periodically check the idle object count for the key in the keyedPool. At most one idle object will be added per period.
224 * If there is an exception when calling {@link KeyedObjectPool#addObject(Object)} then no more checks for that key
225 * will be performed.
226 *
227 * @param keyedPool the keyedPool to check periodically.
228 * @param key the key to check the idle count of.
229 * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
230 * @param period the frequency to check the number of idle objects in a keyedPool, see
231 * {@link Timer#schedule(TimerTask, long, long)}.
232 * @return the {@link TimerTask} that will periodically check the pools idle object count.
233 * @throws IllegalArgumentException when <code>keyedPool</code>, <code>key</code> is <code>null</code> or
234 * when <code>minIdle</code> is negative or when <code>period</code> isn't
235 * valid for {@link Timer#schedule(TimerTask, long, long)}.
236 * @since Pool 1.3
237 */
238 public static TimerTask checkMinIdle(final KeyedObjectPool keyedPool, final Object key, final int minIdle, final long period) throws IllegalArgumentException {
239 if (keyedPool == null) {
240 throw new IllegalArgumentException("keyedPool must not be null.");
241 }
242 if (key == null) {
243 throw new IllegalArgumentException("key must not be null.");
244 }
245 if (minIdle < 0) {
246 throw new IllegalArgumentException("minIdle must be non-negative.");
247 }
248 final TimerTask task = new KeyedObjectPoolMinIdleTimerTask(keyedPool, key, minIdle);
249 getMinIdleTimer().schedule(task, 0L, period);
250 return task;
251 }
252
253 /**
254 * Periodically check the idle object count for each key in the <code>Collection</code> <code>keys</code> in the keyedPool.
255 * At most one idle object will be added per period.
256 *
257 * @param keyedPool the keyedPool to check periodically.
258 * @param keys a collection of keys to check the idle object count.
259 * @param minIdle if the {@link KeyedObjectPool#getNumIdle(Object)} is less than this then add an idle object.
260 * @param period the frequency to check the number of idle objects in a keyedPool, see
261 * {@link Timer#schedule(TimerTask, long, long)}.
262 * @return a {@link Map} of key and {@link TimerTask} pairs that will periodically check the pools idle object count.
263 * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or any of the values in the
264 * collection is <code>null</code> or when <code>minIdle</code> is negative or when <code>period</code> isn't
265 * valid for {@link Timer#schedule(TimerTask, long, long)}.
266 * @see #checkMinIdle(KeyedObjectPool, Object, int, long)
267 * @since Pool 1.3
268 */
269 public static Map checkMinIdle(final KeyedObjectPool keyedPool, final Collection keys, final int minIdle, final long period) throws IllegalArgumentException {
270 if (keys == null) {
271 throw new IllegalArgumentException("keys must not be null.");
272 }
273 final Map tasks = new HashMap(keys.size());
274 final Iterator iter = keys.iterator();
275 while (iter.hasNext()) {
276 final Object key = iter.next();
277 final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
278 tasks.put(key, task);
279 }
280 return tasks;
281 }
282
283 /**
284 * Call <code>addObject()</code> on <code>pool</code> <code>count</code> number of times.
285 *
286 * @param pool the pool to prefill.
287 * @param count the number of idle objects to add.
288 * @throws Exception when {@link ObjectPool#addObject()} fails.
289 * @throws IllegalArgumentException when <code>pool</code> is <code>null</code>.
290 * @since Pool 1.3
291 */
292 public static void prefill(final ObjectPool pool, final int count) throws Exception, IllegalArgumentException {
293 if (pool == null) {
294 throw new IllegalArgumentException("pool must not be null.");
295 }
296 for (int i = 0; i < count; i++) {
297 pool.addObject();
298 }
299 }
300
301 /**
302 * Call <code>addObject(Object)</code> on <code>keyedPool</code> with <code>key</code> <code>count</code>
303 * number of times.
304 *
305 * @param keyedPool the keyedPool to prefill.
306 * @param key the key to add objects for.
307 * @param count the number of idle objects to add for <code>key</code>.
308 * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
309 * @throws IllegalArgumentException when <code>keyedPool</code> or <code>key</code> is <code>null</code>.
310 * @since Pool 1.3
311 */
312 public static void prefill(final KeyedObjectPool keyedPool, final Object key, final int count) throws Exception, IllegalArgumentException {
313 if (keyedPool == null) {
314 throw new IllegalArgumentException("keyedPool must not be null.");
315 }
316 if (key == null) {
317 throw new IllegalArgumentException("key must not be null.");
318 }
319 for (int i = 0; i < count; i++) {
320 keyedPool.addObject(key);
321 }
322 }
323
324 /**
325 * Call <code>addObject(Object)</code> on <code>keyedPool</code> with each key in <code>keys</code> for
326 * <code>count</code> number of times. This has the same effect as calling
327 * {@link #prefill(KeyedObjectPool, Object, int)} for each key in the <code>keys</code> collection.
328 *
329 * @param keyedPool the keyedPool to prefill.
330 * @param keys {@link Collection} of keys to add objects for.
331 * @param count the number of idle objects to add for each <code>key</code>.
332 * @throws Exception when {@link KeyedObjectPool#addObject(Object)} fails.
333 * @throws IllegalArgumentException when <code>keyedPool</code>, <code>keys</code>, or
334 * any value in <code>keys</code> is <code>null</code>.
335 * @see #prefill(KeyedObjectPool, Object, int)
336 * @since Pool 1.3
337 */
338 public static void prefill(final KeyedObjectPool keyedPool, final Collection keys, final int count) throws Exception, IllegalArgumentException {
339 if (keys == null) {
340 throw new IllegalArgumentException("keys must not be null.");
341 }
342 final Iterator iter = keys.iterator();
343 while (iter.hasNext()) {
344 prefill(keyedPool, iter.next(), count);
345 }
346 }
347
348 /**
349 * Returns a synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
350 *
351 * <p><b>Note:</b>
352 * This should not be used on pool implementations that already provide proper synchronization
353 * such as the pools provided in the Commons Pool library. Wrapping a pool that
354 * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
355 * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
356 * </p>
357 *
358 * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
359 * @return a synchronized view of the specified ObjectPool.
360 * @since Pool 1.3
361 */
362 public static ObjectPool synchronizedPool(final ObjectPool pool) {
363 if (pool == null) {
364 throw new IllegalArgumentException("pool must not be null.");
365 }
366 /*
367 assert !(pool instanceof GenericObjectPool)
368 : "GenericObjectPool is already thread-safe";
369 assert !(pool instanceof SoftReferenceObjectPool)
370 : "SoftReferenceObjectPool is already thread-safe";
371 assert !(pool instanceof StackObjectPool)
372 : "StackObjectPool is already thread-safe";
373 assert !"org.apache.commons.pool.composite.CompositeObjectPool".equals(pool.getClass().getName())
374 : "CompositeObjectPools are already thread-safe";
375 */
376 return new SynchronizedObjectPool(pool);
377 }
378
379 /**
380 * Returns a synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
381 *
382 * <p><b>Note:</b>
383 * This should not be used on pool implementations that already provide proper synchronization
384 * such as the pools provided in the Commons Pool library. Wrapping a pool that
385 * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
386 * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
387 * </p>
388 *
389 * @param keyedPool the KeyedObjectPool to be "wrapped" in a synchronized KeyedObjectPool.
390 * @return a synchronized view of the specified KeyedObjectPool.
391 * @since Pool 1.3
392 */
393 public static KeyedObjectPool synchronizedPool(final KeyedObjectPool keyedPool) {
394 if (keyedPool == null) {
395 throw new IllegalArgumentException("keyedPool must not be null.");
396 }
397 /*
398 assert !(keyedPool instanceof GenericKeyedObjectPool)
399 : "GenericKeyedObjectPool is already thread-safe";
400 assert !(keyedPool instanceof StackKeyedObjectPool)
401 : "StackKeyedObjectPool is already thread-safe";
402 assert !"org.apache.commons.pool.composite.CompositeKeyedObjectPool".equals(keyedPool.getClass().getName())
403 : "CompositeKeyedObjectPools are already thread-safe";
404 */
405 return new SynchronizedKeyedObjectPool(keyedPool);
406 }
407
408 /**
409 * Returns a synchronized (thread-safe) PoolableObjectFactory backed by the specified PoolableObjectFactory.
410 *
411 * @param factory the PoolableObjectFactory to be "wrapped" in a synchronized PoolableObjectFactory.
412 * @return a synchronized view of the specified PoolableObjectFactory.
413 * @since Pool 1.3
414 */
415 public static PoolableObjectFactory synchronizedPoolableFactory(final PoolableObjectFactory factory) {
416 return new SynchronizedPoolableObjectFactory(factory);
417 }
418
419 /**
420 * Returns a synchronized (thread-safe) KeyedPoolableObjectFactory backed by the specified KeyedPoolableObjectFactory.
421 *
422 * @param keyedFactory the KeyedPoolableObjectFactory to be "wrapped" in a synchronized KeyedPoolableObjectFactory.
423 * @return a synchronized view of the specified KeyedPoolableObjectFactory.
424 * @since Pool 1.3
425 */
426 public static KeyedPoolableObjectFactory synchronizedPoolableFactory(final KeyedPoolableObjectFactory keyedFactory) {
427 return new SynchronizedKeyedPoolableObjectFactory(keyedFactory);
428 }
429
430 /**
431 * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
432 * This is intended as an always thread-safe alternative to using an idle object evictor
433 * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
434 * pools that experience load spikes.
435 *
436 * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
437 * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
438 * @see #erodingPool(ObjectPool, float)
439 * @since Pool 1.4
440 */
441 public static ObjectPool erodingPool(final ObjectPool pool) {
442 return erodingPool(pool, 1f);
443 }
444
445 /**
446 * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
447 * This is intended as an always thread-safe alternative to using an idle object evictor
448 * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
449 * pools that experience load spikes.
450 *
451 * <p>
452 * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
453 * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
454 * Values greater than 1 cause the pool to less frequently try to shrink it's size.
455 * </p>
456 *
457 * @param pool the ObjectPool to be decorated so it shrinks it's idle count when possible.
458 * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
459 * If 0 < factor < 1 then the pool shrinks more aggressively.
460 * If 1 < factor then the pool shrinks less aggressively.
461 * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
462 * @see #erodingPool(ObjectPool)
463 * @since Pool 1.4
464 */
465 public static ObjectPool erodingPool(final ObjectPool pool, final float factor) {
466 if (pool == null) {
467 throw new IllegalArgumentException("pool must not be null.");
468 }
469 if (factor <= 0f) {
470 throw new IllegalArgumentException("factor must be positive.");
471 }
472 return new ErodingObjectPool(pool, factor);
473 }
474
475 /**
476 * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
477 * This is intended as an always thread-safe alternative to using an idle object evictor
478 * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
479 * pools that experience load spikes.
480 *
481 * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
482 * possible.
483 * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
484 * @see #erodingPool(KeyedObjectPool, float)
485 * @see #erodingPool(KeyedObjectPool, float, boolean)
486 * @since Pool 1.4
487 */
488 public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool) {
489 return erodingPool(keyedPool, 1f);
490 }
491
492 /**
493 * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
494 * This is intended as an always thread-safe alternative to using an idle object evictor
495 * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
496 * pools that experience load spikes.
497 *
498 * <p>
499 * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
500 * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
501 * Values greater than 1 cause the pool to less frequently try to shrink it's size.
502 * </p>
503 *
504 * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
505 * possible.
506 * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
507 * If 0 < factor < 1 then the pool shrinks more aggressively.
508 * If 1 < factor then the pool shrinks less aggressively.
509 * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
510 * @see #erodingPool(KeyedObjectPool, float, boolean)
511 * @since Pool 1.4
512 */
513 public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool, final float factor) {
514 return erodingPool(keyedPool, factor, false);
515 }
516
517 /**
518 * Returns a pool that adaptively decreases it's size when idle objects are no longer needed.
519 * This is intended as an always thread-safe alternative to using an idle object evictor
520 * provided by many pool implementations. This is also an effective way to shrink FIFO ordered
521 * pools that experience load spikes.
522 *
523 * <p>
524 * The factor parameter provides a mechanism to tweak the rate at which the pool tries to shrink
525 * it's size. Values between 0 and 1 cause the pool to try to shrink it's size more often.
526 * Values greater than 1 cause the pool to less frequently try to shrink it's size.
527 * </p>
528 *
529 * <p>
530 * The perKey parameter determines if the pool shrinks on a whole pool basis or a per key basis.
531 * When perKey is false, the keys do not have an effect on the rate at which the pool tries to
532 * shrink it's size. When perKey is true, each key is shrunk independently.
533 * </p>
534 *
535 * @param keyedPool the KeyedObjectPool to be decorated so it shrinks it's idle count when
536 * possible.
537 * @param factor a positive value to scale the rate at which the pool tries to reduce it's size.
538 * If 0 < factor < 1 then the pool shrinks more aggressively.
539 * If 1 < factor then the pool shrinks less aggressively.
540 * @param perKey when true, each key is treated independently.
541 * @return a pool that adaptively decreases it's size when idle objects are no longer needed.
542 * @see #erodingPool(KeyedObjectPool)
543 * @see #erodingPool(KeyedObjectPool, float)
544 * @since Pool 1.4
545 */
546 public static KeyedObjectPool erodingPool(final KeyedObjectPool keyedPool, final float factor, final boolean perKey) {
547 if (keyedPool == null) {
548 throw new IllegalArgumentException("keyedPool must not be null.");
549 }
550 if (factor <= 0f) {
551 throw new IllegalArgumentException("factor must be positive.");
552 }
553 if (perKey) {
554 return new ErodingPerKeyKeyedObjectPool(keyedPool, factor);
555 } else {
556 return new ErodingKeyedObjectPool(keyedPool, factor);
557 }
558 }
559
560 /**
561 * Get the <code>Timer</code> for checking keyedPool's idle count. Lazily create the {@link Timer} as needed.
562 *
563 * @return the {@link Timer} for checking keyedPool's idle count.
564 * @since Pool 1.3
565 */
566 private static synchronized Timer getMinIdleTimer() {
567 if (MIN_IDLE_TIMER == null) {
568 MIN_IDLE_TIMER = new Timer(true);
569 }
570 return MIN_IDLE_TIMER;
571 }
572
573 /**
574 * Adaptor class that wraps and converts a KeyedPoolableObjectFactory with a fixed
575 * key to a PoolableObjectFactory.
576 */
577 private static class PoolableObjectFactoryAdaptor implements PoolableObjectFactory {
578 /** Fixed key */
579 private final Object key;
580
581 /** Wrapped factory */
582 private final KeyedPoolableObjectFactory keyedFactory;
583
584 /**
585 * Create a PoolableObjectFactoryAdaptor wrapping the provided KeyedPoolableObjectFactory with the
586 * given fixed key.
587 *
588 * @param keyedFactory KeyedPoolableObjectFactory that will manage objects
589 * @param key fixed key
590 * @throws IllegalArgumentException if either of the parameters is null
591 */
592 PoolableObjectFactoryAdaptor(final KeyedPoolableObjectFactory keyedFactory, final Object key)
593 throws IllegalArgumentException {
594 if (keyedFactory == null) {
595 throw new IllegalArgumentException("keyedFactory must not be null.");
596 }
597 if (key == null) {
598 throw new IllegalArgumentException("key must not be null.");
599 }
600 this.keyedFactory = keyedFactory;
601 this.key = key;
602 }
603
604 /**
605 * Create an object instance using the configured factory and key.
606 *
607 * @return new object instance
608 */
609 public Object makeObject() throws Exception {
610 return keyedFactory.makeObject(key);
611 }
612
613 /**
614 * Destroy the object, passing the fixed key to the factory.
615 *
616 * @param obj object to destroy
617 */
618 public void destroyObject(final Object obj) throws Exception {
619 keyedFactory.destroyObject(key, obj);
620 }
621
622 /**
623 * Validate the object, passing the fixed key to the factory.
624 *
625 * @param obj object to validate
626 * @return true if validation is successful
627 */
628 public boolean validateObject(final Object obj) {
629 return keyedFactory.validateObject(key, obj);
630 }
631
632 /**
633 * Activate the object, passing the fixed key to the factory.
634 *
635 * @param obj object to activate
636 */
637 public void activateObject(final Object obj) throws Exception {
638 keyedFactory.activateObject(key, obj);
639 }
640
641 /**
642 * Passivate the object, passing the fixed key to the factory.
643 *
644 * @param obj object to passivate
645 */
646 public void passivateObject(final Object obj) throws Exception {
647 keyedFactory.passivateObject(key, obj);
648 }
649
650 /**
651 * {@inheritDoc}
652 */
653 public String toString() {
654 final StringBuffer sb = new StringBuffer();
655 sb.append("PoolableObjectFactoryAdaptor");
656 sb.append("{key=").append(key);
657 sb.append(", keyedFactory=").append(keyedFactory);
658 sb.append('}');
659 return sb.toString();
660 }
661 }
662
663 /**
664 * Adaptor class that turns a PoolableObjectFactory into a KeyedPoolableObjectFactory by
665 * ignoring keys.
666 */
667 private static class KeyedPoolableObjectFactoryAdaptor implements KeyedPoolableObjectFactory {
668
669 /** Underlying PoolableObjectFactory */
670 private final PoolableObjectFactory factory;
671
672 /**
673 * Create a new KeyedPoolableObjectFactoryAdaptor using the given PoolableObjectFactory to
674 * manage objects.
675 *
676 * @param factory wrapped PoolableObjectFactory
677 * @throws IllegalArgumentException if the factory is null
678 */
679 KeyedPoolableObjectFactoryAdaptor(final PoolableObjectFactory factory) throws IllegalArgumentException {
680 if (factory == null) {
681 throw new IllegalArgumentException("factory must not be null.");
682 }
683 this.factory = factory;
684 }
685
686 /**
687 * Create a new object instance, ignoring the key
688 *
689 * @param key ignored
690 * @return newly created object instance
691 */
692 public Object makeObject(final Object key) throws Exception {
693 return factory.makeObject();
694 }
695
696 /**
697 * Destroy the object, ignoring the key.
698 *
699 * @param key ignored
700 * @param obj instance to destroy
701 */
702 public void destroyObject(final Object key, final Object obj) throws Exception {
703 factory.destroyObject(obj);
704 }
705
706 /**
707 * Validate the object, ignoring the key
708 *
709 * @param key ignored
710 * @param obj object to validate
711 * @return true if validation is successful
712 */
713 public boolean validateObject(final Object key, final Object obj) {
714 return factory.validateObject(obj);
715 }
716
717 /**
718 * Activate the object, ignoring the key.
719 *
720 * @param key ignored
721 * @param obj object to be activated
722 */
723 public void activateObject(final Object key, final Object obj) throws Exception {
724 factory.activateObject(obj);
725 }
726
727 /**
728 * Passivate the object, ignoring the key.
729 *
730 * @param key ignored
731 * @param obj object to passivate
732 */
733 public void passivateObject(final Object key, final Object obj) throws Exception {
734 factory.passivateObject(obj);
735 }
736
737 /**
738 * {@inheritDoc}
739 */
740 public String toString() {
741 final StringBuffer sb = new StringBuffer();
742 sb.append("KeyedPoolableObjectFactoryAdaptor");
743 sb.append("{factory=").append(factory);
744 sb.append('}');
745 return sb.toString();
746 }
747 }
748
749 /**
750 * Adapts a KeyedObjectPool to make it an ObjectPool by fixing restricting to
751 * a fixed key.
752 */
753 private static class ObjectPoolAdaptor implements ObjectPool {
754
755 /** Fixed key */
756 private final Object key;
757
758 /** Underlying KeyedObjectPool */
759 private final KeyedObjectPool keyedPool;
760
761 /**
762 * Create a new ObjectPoolAdaptor using the provided KeyedObjectPool and fixed key.
763 *
764 * @param keyedPool underlying KeyedObjectPool
765 * @param key fixed key
766 * @throws IllegalArgumentException if either of the parameters is null
767 */
768 ObjectPoolAdaptor(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
769 if (keyedPool == null) {
770 throw new IllegalArgumentException("keyedPool must not be null.");
771 }
772 if (key == null) {
773 throw new IllegalArgumentException("key must not be null.");
774 }
775 this.keyedPool = keyedPool;
776 this.key = key;
777 }
778
779 /**
780 * {@inheritDoc}
781 */
782 public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
783 return keyedPool.borrowObject(key);
784 }
785
786 /**
787 * {@inheritDoc}
788 */
789 public void returnObject(final Object obj) {
790 try {
791 keyedPool.returnObject(key, obj);
792 } catch (Exception e) {
793 // swallowed as of Pool 2
794 }
795 }
796
797 /**
798 * {@inheritDoc}
799 */
800 public void invalidateObject(final Object obj) {
801 try {
802 keyedPool.invalidateObject(key, obj);
803 } catch (Exception e) {
804 // swallowed as of Pool 2
805 }
806 }
807
808 /**
809 * {@inheritDoc}
810 */
811 public void addObject() throws Exception, IllegalStateException {
812 keyedPool.addObject(key);
813 }
814
815 /**
816 * {@inheritDoc}
817 */
818 public int getNumIdle() throws UnsupportedOperationException {
819 return keyedPool.getNumIdle(key);
820 }
821
822 /**
823 * {@inheritDoc}
824 */
825 public int getNumActive() throws UnsupportedOperationException {
826 return keyedPool.getNumActive(key);
827 }
828
829 /**
830 * {@inheritDoc}
831 */
832 public void clear() throws Exception, UnsupportedOperationException {
833 keyedPool.clear();
834 }
835
836 /**
837 * {@inheritDoc}
838 */
839 public void close() {
840 try {
841 keyedPool.close();
842 } catch (Exception e) {
843 // swallowed as of Pool 2
844 }
845 }
846
847 /**
848 * Sets the PoolableObjectFactory for the pool.
849 *
850 * @param factory new PoolableObjectFactory
851 * @deprecated to be removed in version 2.0
852 */
853 public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
854 keyedPool.setFactory(adapt(factory));
855 }
856
857 /**
858 * {@inheritDoc}
859 */
860 public String toString() {
861 final StringBuffer sb = new StringBuffer();
862 sb.append("ObjectPoolAdaptor");
863 sb.append("{key=").append(key);
864 sb.append(", keyedPool=").append(keyedPool);
865 sb.append('}');
866 return sb.toString();
867 }
868 }
869
870 /**
871 * Adapts an ObjectPool to implement KeyedObjectPool by ignoring key arguments.
872 */
873 private static class KeyedObjectPoolAdaptor implements KeyedObjectPool {
874
875 /** Underlying pool */
876 private final ObjectPool pool;
877
878 /**
879 * Create a new KeyedObjectPoolAdaptor wrapping the given ObjectPool
880 *
881 * @param pool underlying object pool
882 * @throws IllegalArgumentException if pool is null
883 */
884 KeyedObjectPoolAdaptor(final ObjectPool pool) throws IllegalArgumentException {
885 if (pool == null) {
886 throw new IllegalArgumentException("pool must not be null.");
887 }
888 this.pool = pool;
889 }
890
891 /**
892 * Borrow and object from the pool, ignoring the key
893 *
894 * @param key ignored
895 * @return newly created object instance
896 */
897 public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
898 return pool.borrowObject();
899 }
900
901 /**
902 * Return and object to the pool, ignoring the key
903 *
904 * @param key ignored
905 * @param obj object to return
906 */
907 public void returnObject(final Object key, final Object obj) {
908 try {
909 pool.returnObject(obj);
910 } catch (Exception e) {
911 // swallowed as of Pool 2
912 }
913 }
914
915 /**
916 * Invalidate and object, ignoring the key
917 *
918 * @param obj object to invalidate
919 * @param key ignored
920 */
921 public void invalidateObject(final Object key, final Object obj) {
922 try {
923 pool.invalidateObject(obj);
924 } catch (Exception e) {
925 // swallowed as of Pool 2
926 }
927 }
928
929 /**
930 * Add an object to the pool, ignoring the key
931 *
932 * @param key ignored
933 */
934 public void addObject(final Object key) throws Exception, IllegalStateException {
935 pool.addObject();
936 }
937
938 /**
939 * Return the number of objects idle in the pool, ignoring the key.
940 *
941 * @param key ignored
942 * @return idle instance count
943 */
944 public int getNumIdle(final Object key) throws UnsupportedOperationException {
945 return pool.getNumIdle();
946 }
947
948 /**
949 * Return the number of objects checked out from the pool, ignoring the key.
950 *
951 * @param key ignored
952 * @return active instance count
953 */
954 public int getNumActive(final Object key) throws UnsupportedOperationException {
955 return pool.getNumActive();
956 }
957
958 /**
959 * {@inheritDoc}
960 */
961 public int getNumIdle() throws UnsupportedOperationException {
962 return pool.getNumIdle();
963 }
964
965 /**
966 * {@inheritDoc}
967 */
968 public int getNumActive() throws UnsupportedOperationException {
969 return pool.getNumActive();
970 }
971
972 /**
973 * {@inheritDoc}
974 */
975 public void clear() throws Exception, UnsupportedOperationException {
976 pool.clear();
977 }
978
979 /**
980 * Clear the pool, ignoring the key (has same effect as {@link #clear()}.
981 *
982 * @param key ignored.
983 */
984 public void clear(final Object key) throws Exception, UnsupportedOperationException {
985 pool.clear();
986 }
987
988 /**
989 * {@inheritDoc}
990 */
991 public void close() {
992 try {
993 pool.close();
994 } catch (Exception e) {
995 // swallowed as of Pool 2
996 }
997 }
998
999 /**
1000 * Sets the factory used to manage objects.
1001 *
1002 * @param factory new factory to use managing object instances
1003 * @deprecated to be removed in version 2.0
1004 */
1005 public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1006 pool.setFactory(adapt(factory));
1007 }
1008
1009 /**
1010 * {@inheritDoc}
1011 */
1012 public String toString() {
1013 final StringBuffer sb = new StringBuffer();
1014 sb.append("KeyedObjectPoolAdaptor");
1015 sb.append("{pool=").append(pool);
1016 sb.append('}');
1017 return sb.toString();
1018 }
1019 }
1020
1021 /**
1022 * An object pool that performs type checking on objects passed
1023 * to pool methods.
1024 *
1025 */
1026 private static class CheckedObjectPool implements ObjectPool {
1027 /**
1028 * Type of objects allowed in the pool. This should be a subtype of the return type of
1029 * the underlying pool's associated object factory.
1030 */
1031 private final Class type;
1032
1033 /** Underlying object pool */
1034 private final ObjectPool pool;
1035
1036 /**
1037 * Create a CheckedObjectPool accepting objects of the given type using
1038 * the given pool.
1039 *
1040 * @param pool underlying object pool
1041 * @param type expected pooled object type
1042 * @throws IllegalArgumentException if either parameter is null
1043 */
1044 CheckedObjectPool(final ObjectPool pool, final Class type) {
1045 if (pool == null) {
1046 throw new IllegalArgumentException("pool must not be null.");
1047 }
1048 if (type == null) {
1049 throw new IllegalArgumentException("type must not be null.");
1050 }
1051 this.pool = pool;
1052 this.type = type;
1053 }
1054
1055 /**
1056 * Borrow an object from the pool, checking its type.
1057 *
1058 * @return a type-checked object from the pool
1059 * @throws ClassCastException if the object returned by the pool is not of the expected type
1060 */
1061 public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
1062 final Object obj = pool.borrowObject();
1063 if (type.isInstance(obj)) {
1064 return obj;
1065 } else {
1066 throw new ClassCastException("Borrowed object is not of type: " + type.getName() + " was: " + obj);
1067 }
1068 }
1069
1070 /**
1071 * Return an object to the pool, verifying that it is of the correct type.
1072 *
1073 * @param obj object to return
1074 * @throws ClassCastException if obj is not of the expected type
1075 */
1076 public void returnObject(final Object obj) {
1077 if (type.isInstance(obj)) {
1078 try {
1079 pool.returnObject(obj);
1080 } catch (Exception e) {
1081 // swallowed as of Pool 2
1082 }
1083 } else {
1084 throw new ClassCastException("Returned object is not of type: " + type.getName() + " was: " + obj);
1085 }
1086 }
1087
1088 /**
1089 * Invalidates an object from the pool, verifying that it is of the expected type.
1090 *
1091 * @param obj object to invalidate
1092 * @throws ClassCastException if obj is not of the expected type
1093 */
1094 public void invalidateObject(final Object obj) {
1095 if (type.isInstance(obj)) {
1096 try {
1097 pool.invalidateObject(obj);
1098 } catch (Exception e) {
1099 // swallowed as of Pool 2
1100 }
1101 } else {
1102 throw new ClassCastException("Invalidated object is not of type: " + type.getName() + " was: " + obj);
1103 }
1104 }
1105
1106 /**
1107 * {@inheritDoc}
1108 */
1109 public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
1110 pool.addObject();
1111 }
1112
1113 /**
1114 * {@inheritDoc}
1115 */
1116 public int getNumIdle() throws UnsupportedOperationException {
1117 return pool.getNumIdle();
1118 }
1119
1120 /**
1121 * {@inheritDoc}
1122 */
1123 public int getNumActive() throws UnsupportedOperationException {
1124 return pool.getNumActive();
1125 }
1126
1127 /**
1128 * {@inheritDoc}
1129 */
1130 public void clear() throws Exception, UnsupportedOperationException {
1131 pool.clear();
1132 }
1133
1134 /**
1135 * {@inheritDoc}
1136 */
1137 public void close() {
1138 try {
1139 pool.close();
1140 } catch (Exception e) {
1141 // swallowed as of Pool 2
1142 }
1143 }
1144
1145 /**
1146 * Sets the object factory associated with the pool
1147 *
1148 * @param factory object factory
1149 * @deprecated to be removed in version 2.0
1150 */
1151 public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1152 pool.setFactory(factory);
1153 }
1154
1155 /**
1156 * {@inheritDoc}
1157 */
1158 public String toString() {
1159 final StringBuffer sb = new StringBuffer();
1160 sb.append("CheckedObjectPool");
1161 sb.append("{type=").append(type);
1162 sb.append(", pool=").append(pool);
1163 sb.append('}');
1164 return sb.toString();
1165 }
1166 }
1167
1168 /**
1169 * A keyed object pool that performs type checking on objects passed
1170 * to pool methods.
1171 *
1172 */
1173 private static class CheckedKeyedObjectPool implements KeyedObjectPool {
1174 /**
1175 * Expected type of objects managed by the pool. This should be
1176 * a subtype of the return type of the object factory used by the pool.
1177 */
1178 private final Class type;
1179
1180 /** Underlying pool */
1181 private final KeyedObjectPool keyedPool;
1182
1183 /**
1184 * Create a new CheckedKeyedObjectPool from the given pool with given expected object type.
1185 *
1186 * @param keyedPool underlying pool
1187 * @param type expected object type
1188 * @throws IllegalArgumentException if either parameter is null
1189 */
1190 CheckedKeyedObjectPool(final KeyedObjectPool keyedPool, final Class type) {
1191 if (keyedPool == null) {
1192 throw new IllegalArgumentException("keyedPool must not be null.");
1193 }
1194 if (type == null) {
1195 throw new IllegalArgumentException("type must not be null.");
1196 }
1197 this.keyedPool = keyedPool;
1198 this.type = type;
1199 }
1200
1201 /**
1202 * Borrow an object from the pool, verifying correct return type.
1203 *
1204 * @param key pool key
1205 * @return type-checked object from the pool under the given key
1206 * @throws ClassCastException if the object returned by the pool is not of the expected type
1207 */
1208 public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
1209 Object obj = keyedPool.borrowObject(key);
1210 if (type.isInstance(obj)) {
1211 return obj;
1212 } else {
1213 throw new ClassCastException("Borrowed object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
1214 }
1215 }
1216
1217 /**
1218 * Return an object to the pool, checking its type.
1219 *
1220 * @param key the associated key (not type-checked)
1221 * @param obj the object to return (type-checked)
1222 * @throws ClassCastException if obj is not of the expected type
1223 */
1224 public void returnObject(final Object key, final Object obj) {
1225 if (type.isInstance(obj)) {
1226 try {
1227 keyedPool.returnObject(key, obj);
1228 } catch (Exception e) {
1229 // swallowed as of Pool 2
1230 }
1231 } else {
1232 throw new ClassCastException("Returned object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
1233 }
1234 }
1235
1236 /**
1237 * Invalidate an object to the pool, checking its type.
1238 *
1239 * @param key the associated key (not type-checked)
1240 * @param obj the object to return (type-checked)
1241 * @throws ClassCastException if obj is not of the expected type
1242 */
1243 public void invalidateObject(final Object key, final Object obj) {
1244 if (type.isInstance(obj)) {
1245 try {
1246 keyedPool.invalidateObject(key, obj);
1247 } catch (Exception e) {
1248 // swallowed as of Pool 2
1249 }
1250 } else {
1251 throw new ClassCastException("Invalidated object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
1252 }
1253 }
1254
1255 /**
1256 * {@inheritDoc}
1257 */
1258 public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
1259 keyedPool.addObject(key);
1260 }
1261
1262 /**
1263 * {@inheritDoc}
1264 */
1265 public int getNumIdle(final Object key) throws UnsupportedOperationException {
1266 return keyedPool.getNumIdle(key);
1267 }
1268
1269 /**
1270 * {@inheritDoc}
1271 */
1272 public int getNumActive(final Object key) throws UnsupportedOperationException {
1273 return keyedPool.getNumActive(key);
1274 }
1275
1276 /**
1277 * {@inheritDoc}
1278 */
1279 public int getNumIdle() throws UnsupportedOperationException {
1280 return keyedPool.getNumIdle();
1281 }
1282
1283 /**
1284 * {@inheritDoc}
1285 */
1286 public int getNumActive() throws UnsupportedOperationException {
1287 return keyedPool.getNumActive();
1288 }
1289
1290 /**
1291 * {@inheritDoc}
1292 */
1293 public void clear() throws Exception, UnsupportedOperationException {
1294 keyedPool.clear();
1295 }
1296
1297 /**
1298 * {@inheritDoc}
1299 */
1300 public void clear(final Object key) throws Exception, UnsupportedOperationException {
1301 keyedPool.clear(key);
1302 }
1303
1304 /**
1305 * {@inheritDoc}
1306 */
1307 public void close() {
1308 try {
1309 keyedPool.close();
1310 } catch (Exception e) {
1311 // swallowed as of Pool 2
1312 }
1313 }
1314
1315 /**
1316 * Sets the object factory associated with the pool
1317 *
1318 * @param factory object factory
1319 * @deprecated to be removed in version 2.0
1320 */
1321 public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1322 keyedPool.setFactory(factory);
1323 }
1324
1325 /**
1326 * {@inheritDoc}
1327 */
1328 public String toString() {
1329 final StringBuffer sb = new StringBuffer();
1330 sb.append("CheckedKeyedObjectPool");
1331 sb.append("{type=").append(type);
1332 sb.append(", keyedPool=").append(keyedPool);
1333 sb.append('}');
1334 return sb.toString();
1335 }
1336 }
1337
1338 /**
1339 * Timer task that adds objects to the pool until the number of idle
1340 * instances reaches the configured minIdle. Note that this is not the
1341 * same as the pool's minIdle setting.
1342 *
1343 */
1344 private static class ObjectPoolMinIdleTimerTask extends TimerTask {
1345
1346 /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
1347 private final int minIdle;
1348
1349 /** Object pool */
1350 private final ObjectPool pool;
1351
1352 /**
1353 * Create a new ObjectPoolMinIdleTimerTask for the given pool with the given minIdle setting.
1354 *
1355 * @param pool object pool
1356 * @param minIdle number of idle instances to maintain
1357 * @throws IllegalArgumentException if the pool is null
1358 */
1359 ObjectPoolMinIdleTimerTask(final ObjectPool pool, final int minIdle) throws IllegalArgumentException {
1360 if (pool == null) {
1361 throw new IllegalArgumentException("pool must not be null.");
1362 }
1363 this.pool = pool;
1364 this.minIdle = minIdle;
1365 }
1366
1367 /**
1368 * {@inheritDoc}
1369 */
1370 public void run() {
1371 boolean success = false;
1372 try {
1373 if (pool.getNumIdle() < minIdle) {
1374 pool.addObject();
1375 }
1376 success = true;
1377
1378 } catch (Exception e) {
1379 cancel();
1380
1381 } finally {
1382 // detect other types of Throwable and cancel this Timer
1383 if (!success) {
1384 cancel();
1385 }
1386 }
1387 }
1388
1389 /**
1390 * {@inheritDoc}
1391 */
1392 public String toString() {
1393 final StringBuffer sb = new StringBuffer();
1394 sb.append("ObjectPoolMinIdleTimerTask");
1395 sb.append("{minIdle=").append(minIdle);
1396 sb.append(", pool=").append(pool);
1397 sb.append('}');
1398 return sb.toString();
1399 }
1400 }
1401
1402 /**
1403 * Timer task that adds objects to the pool until the number of idle
1404 * instances for the given key reaches the configured minIdle. Note that this is not the
1405 * same as the pool's minIdle setting.
1406 *
1407 */
1408 private static class KeyedObjectPoolMinIdleTimerTask extends TimerTask {
1409 /** Minimum number of idle instances. Not the same as pool.getMinIdle(). */
1410 private final int minIdle;
1411
1412 /** Key to ensure minIdle for */
1413 private final Object key;
1414
1415 /** Keyed object pool */
1416 private final KeyedObjectPool keyedPool;
1417
1418 /**
1419 * Create a new KeyedObjecPoolMinIdleTimerTask.
1420 *
1421 * @param keyedPool keyed object pool
1422 * @param key key to ensure minimum number of idle instances
1423 * @param minIdle minimum number of idle instances
1424 * @throws IllegalArgumentException if the key is null
1425 */
1426 KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool keyedPool, final Object key, final int minIdle) throws IllegalArgumentException {
1427 if (keyedPool == null) {
1428 throw new IllegalArgumentException("keyedPool must not be null.");
1429 }
1430 this.keyedPool = keyedPool;
1431 this.key = key;
1432 this.minIdle = minIdle;
1433 }
1434
1435 /**
1436 * {@inheritDoc}
1437 */
1438 public void run() {
1439 boolean success = false;
1440 try {
1441 if (keyedPool.getNumIdle(key) < minIdle) {
1442 keyedPool.addObject(key);
1443 }
1444 success = true;
1445
1446 } catch (Exception e) {
1447 cancel();
1448
1449 } finally {
1450 // detect other types of Throwable and cancel this Timer
1451 if (!success) {
1452 cancel();
1453 }
1454 }
1455 }
1456
1457 /**
1458 * {@inheritDoc}
1459 */
1460 public String toString() {
1461 final StringBuffer sb = new StringBuffer();
1462 sb.append("KeyedObjectPoolMinIdleTimerTask");
1463 sb.append("{minIdle=").append(minIdle);
1464 sb.append(", key=").append(key);
1465 sb.append(", keyedPool=").append(keyedPool);
1466 sb.append('}');
1467 return sb.toString();
1468 }
1469 }
1470
1471 /**
1472 * A synchronized (thread-safe) ObjectPool backed by the specified ObjectPool.
1473 *
1474 * <p><b>Note:</b>
1475 * This should not be used on pool implementations that already provide proper synchronization
1476 * such as the pools provided in the Commons Pool library. Wrapping a pool that
1477 * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
1478 * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
1479 * </p>
1480 */
1481 private static class SynchronizedObjectPool implements ObjectPool {
1482
1483 /** Object whose monitor is used to synchronize methods on the wrapped pool. */
1484 private final Object lock;
1485
1486 /** the underlying object pool */
1487 private final ObjectPool pool;
1488
1489 /**
1490 * Create a new SynchronizedObjectPool wrapping the given pool.
1491 *
1492 * @param pool the ObjectPool to be "wrapped" in a synchronized ObjectPool.
1493 * @throws IllegalArgumentException if the pool is null
1494 */
1495 SynchronizedObjectPool(final ObjectPool pool) throws IllegalArgumentException {
1496 if (pool == null) {
1497 throw new IllegalArgumentException("pool must not be null.");
1498 }
1499 this.pool = pool;
1500 lock = new Object();
1501 }
1502
1503 /**
1504 * {@inheritDoc}
1505 */
1506 public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
1507 synchronized (lock) {
1508 return pool.borrowObject();
1509 }
1510 }
1511
1512 /**
1513 * {@inheritDoc}
1514 */
1515 public void returnObject(final Object obj) {
1516 synchronized (lock) {
1517 try {
1518 pool.returnObject(obj);
1519 } catch (Exception e) {
1520 // swallowed as of Pool 2
1521 }
1522 }
1523 }
1524
1525 /**
1526 * {@inheritDoc}
1527 */
1528 public void invalidateObject(final Object obj) {
1529 synchronized (lock) {
1530 try {
1531 pool.invalidateObject(obj);
1532 } catch (Exception e) {
1533 // swallowed as of Pool 2
1534 }
1535 }
1536 }
1537
1538 /**
1539 * {@inheritDoc}
1540 */
1541 public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
1542 synchronized (lock) {
1543 pool.addObject();
1544 }
1545 }
1546
1547 /**
1548 * {@inheritDoc}
1549 */
1550 public int getNumIdle() throws UnsupportedOperationException {
1551 synchronized (lock) {
1552 return pool.getNumIdle();
1553 }
1554 }
1555
1556 /**
1557 * {@inheritDoc}
1558 */
1559 public int getNumActive() throws UnsupportedOperationException {
1560 synchronized (lock) {
1561 return pool.getNumActive();
1562 }
1563 }
1564
1565 /**
1566 * {@inheritDoc}
1567 */
1568 public void clear() throws Exception, UnsupportedOperationException {
1569 synchronized (lock) {
1570 pool.clear();
1571 }
1572 }
1573
1574 /**
1575 * {@inheritDoc}
1576 */
1577 public void close() {
1578 try {
1579 synchronized (lock) {
1580 pool.close();
1581 }
1582 } catch (Exception e) {
1583 // swallowed as of Pool 2
1584 }
1585 }
1586
1587 /**
1588 * Sets the factory used by the pool.
1589 *
1590 * @param factory new PoolableObjectFactory
1591 * @deprecated to be removed in pool 2.0
1592 */
1593 public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1594 synchronized (lock) {
1595 pool.setFactory(factory);
1596 }
1597 }
1598
1599 /**
1600 * {@inheritDoc}
1601 */
1602 public String toString() {
1603 final StringBuffer sb = new StringBuffer();
1604 sb.append("SynchronizedObjectPool");
1605 sb.append("{pool=").append(pool);
1606 sb.append('}');
1607 return sb.toString();
1608 }
1609 }
1610
1611 /**
1612 * A synchronized (thread-safe) KeyedObjectPool backed by the specified KeyedObjectPool.
1613 *
1614 * <p><b>Note:</b>
1615 * This should not be used on pool implementations that already provide proper synchronization
1616 * such as the pools provided in the Commons Pool library. Wrapping a pool that
1617 * {@link #wait() waits} for poolable objects to be returned before allowing another one to be
1618 * borrowed with another layer of synchronization will cause liveliness issues or a deadlock.
1619 * </p>
1620 */
1621 private static class SynchronizedKeyedObjectPool implements KeyedObjectPool {
1622
1623 /** Object whose monitor is used to synchronize methods on the wrapped pool. */
1624 private final Object lock;
1625
1626 /** Underlying object pool */
1627 private final KeyedObjectPool keyedPool;
1628
1629 /**
1630 * Create a new SynchronizedKeyedObjectPool wrapping the given pool
1631 *
1632 * @param keyedPool KeyedObjectPool to wrap
1633 * @throws IllegalArgumentException if keyedPool is null
1634 */
1635 SynchronizedKeyedObjectPool(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
1636 if (keyedPool == null) {
1637 throw new IllegalArgumentException("keyedPool must not be null.");
1638 }
1639 this.keyedPool = keyedPool;
1640 lock = new Object();
1641 }
1642
1643 /**
1644 * {@inheritDoc}
1645 */
1646 public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
1647 synchronized (lock) {
1648 return keyedPool.borrowObject(key);
1649 }
1650 }
1651
1652 /**
1653 * {@inheritDoc}
1654 */
1655 public void returnObject(final Object key, final Object obj) {
1656 synchronized (lock) {
1657 try {
1658 keyedPool.returnObject(key, obj);
1659 } catch (Exception e) {
1660 // swallowed
1661 }
1662 }
1663 }
1664
1665 /**
1666 * {@inheritDoc}
1667 */
1668 public void invalidateObject(final Object key, final Object obj) {
1669 synchronized (lock) {
1670 try {
1671 keyedPool.invalidateObject(key, obj);
1672 } catch (Exception e) {
1673 // swallowed as of Pool 2
1674 }
1675 }
1676 }
1677
1678 /**
1679 * {@inheritDoc}
1680 */
1681 public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
1682 synchronized (lock) {
1683 keyedPool.addObject(key);
1684 }
1685 }
1686
1687 /**
1688 * {@inheritDoc}
1689 */
1690 public int getNumIdle(final Object key) throws UnsupportedOperationException {
1691 synchronized (lock) {
1692 return keyedPool.getNumIdle(key);
1693 }
1694 }
1695
1696 /**
1697 * {@inheritDoc}
1698 */
1699 public int getNumActive(final Object key) throws UnsupportedOperationException {
1700 synchronized (lock) {
1701 return keyedPool.getNumActive(key);
1702 }
1703 }
1704
1705 /**
1706 * {@inheritDoc}
1707 */
1708 public int getNumIdle() throws UnsupportedOperationException {
1709 synchronized (lock) {
1710 return keyedPool.getNumIdle();
1711 }
1712 }
1713
1714 /**
1715 * {@inheritDoc}
1716 */
1717 public int getNumActive() throws UnsupportedOperationException {
1718 synchronized (lock) {
1719 return keyedPool.getNumActive();
1720 }
1721 }
1722
1723 /**
1724 * {@inheritDoc}
1725 */
1726 public void clear() throws Exception, UnsupportedOperationException {
1727 synchronized (lock) {
1728 keyedPool.clear();
1729 }
1730 }
1731
1732 /**
1733 * {@inheritDoc}
1734 */
1735 public void clear(final Object key) throws Exception, UnsupportedOperationException {
1736 synchronized (lock) {
1737 keyedPool.clear(key);
1738 }
1739 }
1740
1741 /**
1742 * {@inheritDoc}
1743 */
1744 public void close() {
1745 try {
1746 synchronized (lock) {
1747 keyedPool.close();
1748 }
1749 } catch (Exception e) {
1750 // swallowed as of Pool 2
1751 }
1752 }
1753
1754 /**
1755 * Sets the object factory used by the pool.
1756 *
1757 * @param factory KeyedPoolableObjectFactory used by the pool
1758 * @deprecated to be removed in pool 2.0
1759 */
1760 public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
1761 synchronized (lock) {
1762 keyedPool.setFactory(factory);
1763 }
1764 }
1765
1766 /**
1767 * {@inheritDoc}
1768 */
1769 public String toString() {
1770 final StringBuffer sb = new StringBuffer();
1771 sb.append("SynchronizedKeyedObjectPool");
1772 sb.append("{keyedPool=").append(keyedPool);
1773 sb.append('}');
1774 return sb.toString();
1775 }
1776 }
1777
1778 /**
1779 * A fully synchronized PoolableObjectFactory that wraps a PoolableObjectFactory and synchronizes
1780 * access to the wrapped factory methods.
1781 *
1782 * <p><b>Note:</b>
1783 * This should not be used on pool implementations that already provide proper synchronization
1784 * such as the pools provided in the Commons Pool library. </p>
1785 */
1786 private static class SynchronizedPoolableObjectFactory implements PoolableObjectFactory {
1787 /** Synchronization lock */
1788 private final Object lock;
1789
1790 /** Wrapped factory */
1791 private final PoolableObjectFactory factory;
1792
1793 /**
1794 * Create a SynchronizedPoolableObjectFactory wrapping the given factory.
1795 *
1796 * @param factory underlying factory to wrap
1797 * @throws IllegalArgumentException if the factory is null
1798 */
1799 SynchronizedPoolableObjectFactory(final PoolableObjectFactory factory) throws IllegalArgumentException {
1800 if (factory == null) {
1801 throw new IllegalArgumentException("factory must not be null.");
1802 }
1803 this.factory = factory;
1804 lock = new Object();
1805 }
1806
1807 /**
1808 * {@inheritDoc}
1809 */
1810 public Object makeObject() throws Exception {
1811 synchronized (lock) {
1812 return factory.makeObject();
1813 }
1814 }
1815
1816 /**
1817 * {@inheritDoc}
1818 */
1819 public void destroyObject(final Object obj) throws Exception {
1820 synchronized (lock) {
1821 factory.destroyObject(obj);
1822 }
1823 }
1824
1825 /**
1826 * {@inheritDoc}
1827 */
1828 public boolean validateObject(final Object obj) {
1829 synchronized (lock) {
1830 return factory.validateObject(obj);
1831 }
1832 }
1833
1834 /**
1835 * {@inheritDoc}
1836 */
1837 public void activateObject(final Object obj) throws Exception {
1838 synchronized (lock) {
1839 factory.activateObject(obj);
1840 }
1841 }
1842
1843 /**
1844 * {@inheritDoc}
1845 */
1846 public void passivateObject(final Object obj) throws Exception {
1847 synchronized (lock) {
1848 factory.passivateObject(obj);
1849 }
1850 }
1851
1852 /**
1853 * {@inheritDoc}
1854 */
1855 public String toString() {
1856 final StringBuffer sb = new StringBuffer();
1857 sb.append("SynchronizedPoolableObjectFactory");
1858 sb.append("{factory=").append(factory);
1859 sb.append('}');
1860 return sb.toString();
1861 }
1862 }
1863
1864 /**
1865 * A fully synchronized KeyedPoolableObjectFactory that wraps a KeyedPoolableObjectFactory and synchronizes
1866 * access to the wrapped factory methods.
1867 *
1868 * <p><b>Note:</b>
1869 * This should not be used on pool implementations that already provide proper synchronization
1870 * such as the pools provided in the Commons Pool library. </p>
1871 */
1872 private static class SynchronizedKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory {
1873 /** Synchronization lock */
1874 private final Object lock;
1875
1876 /** Wrapped factory */
1877 private final KeyedPoolableObjectFactory keyedFactory;
1878
1879 /**
1880 * Create a SynchronizedKeyedPoolableObjectFactory wrapping the given factory.
1881 *
1882 * @param keyedFactory underlying factory to wrap
1883 * @throws IllegalArgumentException if the factory is null
1884 */
1885 SynchronizedKeyedPoolableObjectFactory(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
1886 if (keyedFactory == null) {
1887 throw new IllegalArgumentException("keyedFactory must not be null.");
1888 }
1889 this.keyedFactory = keyedFactory;
1890 lock = new Object();
1891 }
1892
1893 /**
1894 * {@inheritDoc}
1895 */
1896 public Object makeObject(final Object key) throws Exception {
1897 synchronized (lock) {
1898 return keyedFactory.makeObject(key);
1899 }
1900 }
1901
1902 /**
1903 * {@inheritDoc}
1904 */
1905 public void destroyObject(final Object key, final Object obj) throws Exception {
1906 synchronized (lock) {
1907 keyedFactory.destroyObject(key, obj);
1908 }
1909 }
1910
1911 /**
1912 * {@inheritDoc}
1913 */
1914 public boolean validateObject(final Object key, final Object obj) {
1915 synchronized (lock) {
1916 return keyedFactory.validateObject(key, obj);
1917 }
1918 }
1919
1920 /**
1921 * {@inheritDoc}
1922 */
1923 public void activateObject(final Object key, final Object obj) throws Exception {
1924 synchronized (lock) {
1925 keyedFactory.activateObject(key, obj);
1926 }
1927 }
1928
1929 /**
1930 * {@inheritDoc}
1931 */
1932 public void passivateObject(final Object key, final Object obj) throws Exception {
1933 synchronized (lock) {
1934 keyedFactory.passivateObject(key, obj);
1935 }
1936 }
1937
1938 /**
1939 * {@inheritDoc}
1940 */
1941 public String toString() {
1942 final StringBuffer sb = new StringBuffer();
1943 sb.append("SynchronizedKeyedPoolableObjectFactory");
1944 sb.append("{keyedFactory=").append(keyedFactory);
1945 sb.append('}');
1946 return sb.toString();
1947 }
1948 }
1949
1950 /**
1951 * Encapsulate the logic for when the next poolable object should be discarded.
1952 * Each time update is called, the next time to shrink is recomputed, based on
1953 * the float factor, number of idle instances in the pool and high water mark.
1954 * Float factor is assumed to be between 0 and 1. Values closer to 1 cause
1955 * less frequent erosion events. Erosion event timing also depends on numIdle.
1956 * When this value is relatively high (close to previously established high water
1957 * mark), erosion occurs more frequently.
1958 */
1959 private static class ErodingFactor {
1960 /** Determines frequency of "erosion" events */
1961 private final float factor;
1962
1963 /** Time of next shrink event */
1964 private transient volatile long nextShrink;
1965
1966 /** High water mark - largest numIdle encountered */
1967 private transient volatile int idleHighWaterMark;
1968
1969 /**
1970 * Create a new ErodingFactor with the given erosion factor.
1971 *
1972 * @param factor erosion factor
1973 */
1974 public ErodingFactor(final float factor) {
1975 this.factor = factor;
1976 nextShrink = System.currentTimeMillis() + (long)(900000 * factor); // now + 15 min * factor
1977 idleHighWaterMark = 1;
1978 }
1979
1980 /**
1981 * Updates internal state based on numIdle and the current time.
1982 *
1983 * @param numIdle number of idle elements in the pool
1984 */
1985 public void update(final int numIdle) {
1986 update(System.currentTimeMillis(), numIdle);
1987 }
1988
1989 /**
1990 * Updates internal state using the supplied time and numIdle.
1991 *
1992 * @param now current time
1993 * @param numIdle number of idle elements in the pool
1994 */
1995 public void update(final long now, final int numIdle) {
1996 final int idle = Math.max(0, numIdle);
1997 idleHighWaterMark = Math.max(idle, idleHighWaterMark);
1998 final float maxInterval = 15f;
1999 final float minutes = maxInterval + ((1f-maxInterval)/idleHighWaterMark) * idle;
2000 nextShrink = now + (long)(minutes * 60000f * factor);
2001 }
2002
2003 /**
2004 * Returns the time of the next erosion event.
2005 *
2006 * @return next shrink time
2007 */
2008 public long getNextShrink() {
2009 return nextShrink;
2010 }
2011
2012 /**
2013 * {@inheritDoc}
2014 */
2015 public String toString() {
2016 return "ErodingFactor{" +
2017 "factor=" + factor +
2018 ", idleHighWaterMark=" + idleHighWaterMark +
2019 '}';
2020 }
2021 }
2022
2023 /**
2024 * Decorates an object pool, adding "eroding" behavior. Based on the
2025 * configured {@link #factor erosion factor}, objects returning to the pool
2026 * may be invalidated instead of being added to idle capacity.
2027 *
2028 */
2029 private static class ErodingObjectPool implements ObjectPool {
2030 /** Underlying object pool */
2031 private final ObjectPool pool;
2032
2033 /** Erosion factor */
2034 private final ErodingFactor factor;
2035
2036 /**
2037 * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
2038 *
2039 * @param pool underlying pool
2040 * @param factor erosion factor - determines the frequency of erosion events
2041 * @see #factor
2042 */
2043 public ErodingObjectPool(final ObjectPool pool, final float factor) {
2044 this.pool = pool;
2045 this.factor = new ErodingFactor(factor);
2046 }
2047
2048 /**
2049 * {@inheritDoc}
2050 */
2051 public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
2052 return pool.borrowObject();
2053 }
2054
2055 /**
2056 * Returns obj to the pool, unless erosion is triggered, in which
2057 * case obj is invalidated. Erosion is triggered when there are idle instances in
2058 * the pool and more than the {@link #factor erosion factor}-determined time has elapsed
2059 * since the last returnObject activation.
2060 *
2061 * @param obj object to return or invalidate
2062 * @see #factor
2063 */
2064 public void returnObject(final Object obj) {
2065 boolean discard = false;
2066 final long now = System.currentTimeMillis();
2067 synchronized (pool) {
2068 if (factor.getNextShrink() < now) { // XXX: Pool 3: move test out of sync block
2069 final int numIdle = pool.getNumIdle();
2070 if (numIdle > 0) {
2071 discard = true;
2072 }
2073
2074 factor.update(now, numIdle);
2075 }
2076 }
2077 try {
2078 if (discard) {
2079 pool.invalidateObject(obj);
2080 } else {
2081 pool.returnObject(obj);
2082 }
2083 } catch (Exception e) {
2084 // swallowed
2085 }
2086 }
2087
2088 /**
2089 * {@inheritDoc}
2090 */
2091 public void invalidateObject(final Object obj) {
2092 try {
2093 pool.invalidateObject(obj);
2094 } catch (Exception e) {
2095 // swallowed
2096 }
2097 }
2098
2099 /**
2100 * {@inheritDoc}
2101 */
2102 public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
2103 pool.addObject();
2104 }
2105
2106 /**
2107 * {@inheritDoc}
2108 */
2109 public int getNumIdle() throws UnsupportedOperationException {
2110 return pool.getNumIdle();
2111 }
2112
2113 /**
2114 * {@inheritDoc}
2115 */
2116 public int getNumActive() throws UnsupportedOperationException {
2117 return pool.getNumActive();
2118 }
2119
2120 /**
2121 * {@inheritDoc}
2122 */
2123 public void clear() throws Exception, UnsupportedOperationException {
2124 pool.clear();
2125 }
2126
2127 /**
2128 * {@inheritDoc}
2129 */
2130 public void close() {
2131 try {
2132 pool.close();
2133 } catch (Exception e) {
2134 // swallowed
2135 }
2136 }
2137
2138 /**
2139 * {@inheritDoc}
2140 * @deprecated to be removed in pool 2.0
2141 */
2142 public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
2143 pool.setFactory(factory);
2144 }
2145
2146 /**
2147 * {@inheritDoc}
2148 */
2149 public String toString() {
2150 return "ErodingObjectPool{" +
2151 "factor=" + factor +
2152 ", pool=" + pool +
2153 '}';
2154 }
2155 }
2156
2157 /**
2158 * Decorates a keyed object pool, adding "eroding" behavior. Based on the
2159 * configured {@link #factor erosion factor}, objects returning to the pool
2160 * may be invalidated instead of being added to idle capacity.
2161 *
2162 */
2163 private static class ErodingKeyedObjectPool implements KeyedObjectPool {
2164 /** Underlying pool */
2165 private final KeyedObjectPool keyedPool;
2166
2167 /** Erosion factor */
2168 private final ErodingFactor erodingFactor;
2169
2170 /**
2171 * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
2172 *
2173 * @param keyedPool underlying pool
2174 * @param factor erosion factor - determines the frequency of erosion events
2175 * @see #erodingFactor
2176 */
2177 public ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
2178 this(keyedPool, new ErodingFactor(factor));
2179 }
2180
2181 /**
2182 * Create an ErodingObjectPool wrapping the given pool using the specified erosion factor.
2183 *
2184 * @param keyedPool underlying pool - must not be null
2185 * @param erodingFactor erosion factor - determines the frequency of erosion events
2186 * @see #factor
2187 */
2188 protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final ErodingFactor erodingFactor) {
2189 if (keyedPool == null) {
2190 throw new IllegalArgumentException("keyedPool must not be null.");
2191 }
2192 this.keyedPool = keyedPool;
2193 this.erodingFactor = erodingFactor;
2194 }
2195
2196 /**
2197 * {@inheritDoc}
2198 */
2199 public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
2200 return keyedPool.borrowObject(key);
2201 }
2202
2203 /**
2204 * Returns obj to the pool, unless erosion is triggered, in which
2205 * case obj is invalidated. Erosion is triggered when there are idle instances in
2206 * the pool associated with the given key and more than the configured {@link #erodingFactor erosion factor}
2207 * time has elapsed since the last returnObject activation.
2208 *
2209 * @param obj object to return or invalidate
2210 * @param key key
2211 * @see #erodingFactor
2212 */
2213 public void returnObject(final Object key, final Object obj) throws Exception {
2214 boolean discard = false;
2215 final long now = System.currentTimeMillis();
2216 final ErodingFactor factor = getErodingFactor(key);
2217 synchronized (keyedPool) {
2218 if (factor.getNextShrink() < now) {
2219 final int numIdle = numIdle(key);
2220 if (numIdle > 0) {
2221 discard = true;
2222 }
2223
2224 factor.update(now, numIdle);
2225 }
2226 }
2227 try {
2228 if (discard) {
2229 keyedPool.invalidateObject(key, obj);
2230 } else {
2231 keyedPool.returnObject(key, obj);
2232 }
2233 } catch (Exception e) {
2234 // swallowed
2235 }
2236 }
2237
2238 /**
2239 * {@inheritDoc}
2240 */
2241 protected int numIdle(final Object key) {
2242 return getKeyedPool().getNumIdle();
2243 }
2244
2245 /**
2246 * Returns the eroding factor for the given key
2247 * @param key key
2248 * @return eroding factor for the given keyed pool
2249 */
2250 protected ErodingFactor getErodingFactor(final Object key) {
2251 return erodingFactor;
2252 }
2253
2254 /**
2255 * {@inheritDoc}
2256 */
2257 public void invalidateObject(final Object key, final Object obj) {
2258 try {
2259 keyedPool.invalidateObject(key, obj);
2260 } catch (Exception e) {
2261 // swallowed
2262 }
2263 }
2264
2265 /**
2266 * {@inheritDoc}
2267 */
2268 public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
2269 keyedPool.addObject(key);
2270 }
2271
2272 /**
2273 * {@inheritDoc}
2274 */
2275 public int getNumIdle() throws UnsupportedOperationException {
2276 return keyedPool.getNumIdle();
2277 }
2278
2279 /**
2280 * {@inheritDoc}
2281 */
2282 public int getNumIdle(final Object key) throws UnsupportedOperationException {
2283 return keyedPool.getNumIdle(key);
2284 }
2285
2286 /**
2287 * {@inheritDoc}
2288 */
2289 public int getNumActive() throws UnsupportedOperationException {
2290 return keyedPool.getNumActive();
2291 }
2292
2293 /**
2294 * {@inheritDoc}
2295 */
2296 public int getNumActive(final Object key) throws UnsupportedOperationException {
2297 return keyedPool.getNumActive(key);
2298 }
2299
2300 /**
2301 * {@inheritDoc}
2302 */
2303 public void clear() throws Exception, UnsupportedOperationException {
2304 keyedPool.clear();
2305 }
2306
2307 /**
2308 * {@inheritDoc}
2309 */
2310 public void clear(final Object key) throws Exception, UnsupportedOperationException {
2311 keyedPool.clear(key);
2312 }
2313
2314 /**
2315 * {@inheritDoc}
2316 */
2317 public void close() {
2318 try {
2319 keyedPool.close();
2320 } catch (Exception e) {
2321 // swallowed
2322 }
2323 }
2324
2325 /**
2326 * {@inheritDoc}
2327 * @deprecated to be removed in pool 2.0
2328 */
2329 public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
2330 keyedPool.setFactory(factory);
2331 }
2332
2333 /**
2334 * Returns the underlying pool
2335 *
2336 * @return the keyed pool that this ErodingKeyedObjectPool wraps
2337 */
2338 protected KeyedObjectPool getKeyedPool() {
2339 return keyedPool;
2340 }
2341
2342 /**
2343 * {@inheritDoc}
2344 */
2345 public String toString() {
2346 return "ErodingKeyedObjectPool{" +
2347 "erodingFactor=" + erodingFactor +
2348 ", keyedPool=" + keyedPool +
2349 '}';
2350 }
2351 }
2352
2353 /**
2354 * Extends ErodingKeyedObjectPool to allow erosion to take place on a per-key
2355 * basis. Timing of erosion events is tracked separately for separate keyed pools.
2356 */
2357 private static class ErodingPerKeyKeyedObjectPool extends ErodingKeyedObjectPool {
2358 /** Erosion factor - same for all pools */
2359 private final float factor;
2360
2361 /** Map of ErodingFactor instances keyed on pool keys */
2362 private final Map factors = Collections.synchronizedMap(new HashMap());
2363
2364 /**
2365 * Create a new ErordingPerKeyKeyedObjectPool decorating the given keyed pool with
2366 * the specified erosion factor.
2367 * @param keyedPool underlying keyed pool
2368 * @param factor erosion factor
2369 */
2370 public ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
2371 super(keyedPool, null);
2372 this.factor = factor;
2373 }
2374
2375 /**
2376 * {@inheritDoc}
2377 */
2378 protected int numIdle(final Object key) {
2379 return getKeyedPool().getNumIdle(key);
2380 }
2381
2382 /**
2383 * {@inheritDoc}
2384 */
2385 protected ErodingFactor getErodingFactor(final Object key) {
2386 ErodingFactor factor = (ErodingFactor)factors.get(key);
2387 // this may result in two ErodingFactors being created for a key
2388 // since they are small and cheap this is okay.
2389 if (factor == null) {
2390 factor = new ErodingFactor(this.factor);
2391 factors.put(key, factor);
2392 }
2393 return factor;
2394 }
2395
2396 /**
2397 * {@inheritDoc}
2398 */
2399 public String toString() {
2400 return "ErodingPerKeyKeyedObjectPool{" +
2401 "factor=" + factor +
2402 ", keyedPool=" + getKeyedPool() +
2403 '}';
2404 }
2405 }
2406 }