Coverage Report - org.apache.commons.pool.PoolUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
PoolUtils
0%
0/88
0%
0/52
1.767
PoolUtils$CheckedKeyedObjectPool
0%
0/46
0%
0/10
1.767
PoolUtils$CheckedObjectPool
0%
0/42
0%
0/10
1.767
PoolUtils$ErodingFactor
0%
0/15
N/A
1.767
PoolUtils$ErodingKeyedObjectPool
0%
0/49
0%
0/8
1.767
PoolUtils$ErodingObjectPool
0%
0/37
0%
0/6
1.767
PoolUtils$ErodingPerKeyKeyedObjectPool
0%
0/11
0%
0/2
1.767
PoolUtils$KeyedObjectPoolAdaptor
0%
0/35
0%
0/2
1.767
PoolUtils$KeyedObjectPoolMinIdleTimerTask
0%
0/23
0%
0/6
1.767
PoolUtils$KeyedPoolableObjectFactoryAdaptor
0%
0/18
0%
0/2
1.767
PoolUtils$ObjectPoolAdaptor
0%
0/35
0%
0/4
1.767
PoolUtils$ObjectPoolMinIdleTimerTask
0%
0/21
0%
0/6
1.767
PoolUtils$PoolableObjectFactoryAdaptor
0%
0/22
0%
0/4
1.767
PoolUtils$SynchronizedKeyedObjectPool
0%
0/60
0%
0/2
1.767
PoolUtils$SynchronizedKeyedPoolableObjectFactory
0%
0/29
0%
0/2
1.767
PoolUtils$SynchronizedObjectPool
0%
0/50
0%
0/2
1.767
PoolUtils$SynchronizedPoolableObjectFactory
0%
0/29
0%
0/2
1.767
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.pool;
 19  
 
 20  
 import java.util.Collection;
 21  
 import java.util.HashMap;
 22  
 import java.util.Iterator;
 23  
 import java.util.Map;
 24  
 import java.util.NoSuchElementException;
 25  
 import java.util.Timer;
 26  
 import java.util.TimerTask;
 27  
 import java.util.Collections;
 28  
 
 29  
 /**
 30  
  * This class consists exclusively of static methods that operate on or return ObjectPool
 31  
  * or KeyedObjectPool related interfaces.
 32  
  *
 33  
  * @author Sandy McArthur
 34  
  * @version $Revision: 777748 $ $Date: 2009-05-22 20:00:44 -0400 (Fri, 22 May 2009) $
 35  
  * @since Pool 1.3
 36  
  */
 37  
 public final class PoolUtils {
 38  
 
 39  
     /**
 40  
      * Timer used to periodically check pools idle object count.
 41  
      * Because a {@link Timer} creates a {@link Thread} this is lazily instantiated.
 42  
      */
 43  
     private static Timer MIN_IDLE_TIMER;
 44  
 
 45  
     /**
 46  
      * PoolUtils instances should NOT be constructed in standard programming.
 47  
      * Instead, the class should be used procedurally: PoolUtils.adapt(aPool);.
 48  
      * This constructor is public to permit tools that require a JavaBean instance to operate.
 49  
      */
 50  0
     public PoolUtils() {
 51  0
     }
 52  
 
 53  
     /**
 54  
      * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
 55  
      * needed. This method is the equivalent of calling
 56  
      * {@link #adapt(KeyedPoolableObjectFactory, Object) PoolUtils.adapt(aKeyedPoolableObjectFactory, new Object())}.
 57  
      *
 58  
      * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
 59  
      * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with an internal key.
 60  
      * @throws IllegalArgumentException when <code>keyedFactory</code> is <code>null</code>.
 61  
      * @see #adapt(KeyedPoolableObjectFactory, Object)
 62  
      * @since Pool 1.3
 63  
      */
 64  
     public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
 65  0
         return adapt(keyedFactory, new Object());
 66  
     }
 67  
 
 68  
     /**
 69  
      * Adapt a <code>KeyedPoolableObjectFactory</code> instance to work where a <code>PoolableObjectFactory</code> is
 70  
      * needed using the specified <code>key</code> when delegating.
 71  
      *
 72  
      * @param keyedFactory the {@link KeyedPoolableObjectFactory} to delegate to.
 73  
      * @param key the key to use when delegating.
 74  
      * @return a {@link PoolableObjectFactory} that delegates to <code>keyedFactory</code> with the specified key.
 75  
      * @throws IllegalArgumentException when <code>keyedFactory</code> or <code>key</code> is <code>null</code>.
 76  
      * @see #adapt(KeyedPoolableObjectFactory)
 77  
      * @since Pool 1.3
 78  
      */
 79  
     public static PoolableObjectFactory adapt(final KeyedPoolableObjectFactory keyedFactory, final Object key) throws IllegalArgumentException {
 80  0
         return new PoolableObjectFactoryAdaptor(keyedFactory, key);
 81  
     }
 82  
 
 83  
     /**
 84  
      * Adapt a <code>PoolableObjectFactory</code> instance to work where a <code>KeyedPoolableObjectFactory</code> is
 85  
      * needed. The key is ignored.
 86  
      *
 87  
      * @param factory the {@link PoolableObjectFactory} to delegate to.
 88  
      * @return a {@link KeyedPoolableObjectFactory} that delegates to <code>factory</code> ignoring the key.
 89  
      * @throws IllegalArgumentException when <code>factory</code> is <code>null</code>.
 90  
      * @since Pool 1.3
 91  
      */
 92  
     public static KeyedPoolableObjectFactory adapt(final PoolableObjectFactory factory) throws IllegalArgumentException {
 93  0
         return new KeyedPoolableObjectFactoryAdaptor(factory);
 94  
     }
 95  
 
 96  
     /**
 97  
      * Adapt a <code>KeyedObjectPool</code> instance to work where an <code>ObjectPool</code> is needed. This is the
 98  
      * equivalent of calling {@link #adapt(KeyedObjectPool, Object) PoolUtils.adapt(aKeyedObjectPool, new Object())}.
 99  
      *
 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  0
         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  0
         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  0
         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  0
         if (pool == null) {
 149  0
             throw new IllegalArgumentException("pool must not be null.");
 150  
         }
 151  0
         if (type == null) {
 152  0
             throw new IllegalArgumentException("type must not be null.");
 153  
         }
 154  0
         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  0
         if (keyedPool == null) {
 168  0
             throw new IllegalArgumentException("keyedPool must not be null.");
 169  
         }
 170  0
         if (type == null) {
 171  0
             throw new IllegalArgumentException("type must not be null.");
 172  
         }
 173  0
         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  0
         if (pool == null) {
 192  0
             throw new IllegalArgumentException("keyedPool must not be null.");
 193  
         }
 194  0
         if (minIdle < 0) {
 195  0
             throw new IllegalArgumentException("minIdle must be non-negative.");
 196  
         }
 197  0
         final TimerTask task = new ObjectPoolMinIdleTimerTask(pool, minIdle);
 198  0
         getMinIdleTimer().schedule(task, 0L, period);
 199  0
         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  0
         if (keyedPool == null) {
 220  0
             throw new IllegalArgumentException("keyedPool must not be null.");
 221  
         }
 222  0
         if (key == null) {
 223  0
             throw new IllegalArgumentException("key must not be null.");
 224  
         }
 225  0
         if (minIdle < 0) {
 226  0
             throw new IllegalArgumentException("minIdle must be non-negative.");
 227  
         }
 228  0
         final TimerTask task = new KeyedObjectPoolMinIdleTimerTask(keyedPool, key, minIdle);
 229  0
         getMinIdleTimer().schedule(task, 0L, period);
 230  0
         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  0
         if (keys == null) {
 251  0
             throw new IllegalArgumentException("keys must not be null.");
 252  
         }
 253  0
         final Map tasks = new HashMap(keys.size());
 254  0
         final Iterator iter = keys.iterator();
 255  0
         while (iter.hasNext()) {
 256  0
             final Object key = iter.next();
 257  0
             final TimerTask task = checkMinIdle(keyedPool, key, minIdle, period);
 258  0
             tasks.put(key, task);
 259  0
         }
 260  0
         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  0
         if (pool == null) {
 274  0
             throw new IllegalArgumentException("pool must not be null.");
 275  
         }
 276  0
         for (int i = 0; i < count; i++) {
 277  0
             pool.addObject();
 278  
         }
 279  0
     }
 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  0
         if (keyedPool == null) {
 294  0
             throw new IllegalArgumentException("keyedPool must not be null.");
 295  
         }
 296  0
         if (key == null) {
 297  0
             throw new IllegalArgumentException("key must not be null.");
 298  
         }
 299  0
         for (int i = 0; i < count; i++) {
 300  0
             keyedPool.addObject(key);
 301  
         }
 302  0
     }
 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  0
         if (keys == null) {
 320  0
             throw new IllegalArgumentException("keys must not be null.");
 321  
         }
 322  0
         final Iterator iter = keys.iterator();
 323  0
         while (iter.hasNext()) {
 324  0
             prefill(keyedPool, iter.next(), count);
 325  
         }
 326  0
     }
 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  0
         if (pool == null) {
 344  0
             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  0
         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  0
         if (keyedPool == null) {
 375  0
             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  0
         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  0
         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  0
         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  0
         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 &lt; factor &lt; 1 then the pool shrinks more aggressively.
 440  
      * If 1 &lt; 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  0
         if (pool == null) {
 447  0
             throw new IllegalArgumentException("pool must not be null.");
 448  
         }
 449  0
         if (factor <= 0f) {
 450  0
             throw new IllegalArgumentException("factor must be positive.");
 451  
         }
 452  0
         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  0
         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 &lt; factor &lt; 1 then the pool shrinks more aggressively.
 488  
      * If 1 &lt; 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  0
         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 &lt; factor &lt; 1 then the pool shrinks more aggressively.
 519  
      * If 1 &lt; 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  0
         if (keyedPool == null) {
 528  0
             throw new IllegalArgumentException("keyedPool must not be null.");
 529  
         }
 530  0
         if (factor <= 0f) {
 531  0
             throw new IllegalArgumentException("factor must be positive.");
 532  
         }
 533  0
         if (perKey) {
 534  0
             return new ErodingPerKeyKeyedObjectPool(keyedPool, factor);
 535  
         } else {
 536  0
             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  0
         if (MIN_IDLE_TIMER == null) {
 548  0
             MIN_IDLE_TIMER = new Timer(true);
 549  
         }
 550  0
         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  0
         PoolableObjectFactoryAdaptor(final KeyedPoolableObjectFactory keyedFactory, final Object key) throws IllegalArgumentException {
 558  0
             if (keyedFactory == null) {
 559  0
                 throw new IllegalArgumentException("keyedFactory must not be null.");
 560  
             }
 561  0
             if (key == null) {
 562  0
                 throw new IllegalArgumentException("key must not be null.");
 563  
             }
 564  0
             this.keyedFactory = keyedFactory;
 565  0
             this.key = key;
 566  0
         }
 567  
 
 568  
         public Object makeObject() throws Exception {
 569  0
             return keyedFactory.makeObject(key);
 570  
         }
 571  
 
 572  
         public void destroyObject(final Object obj) throws Exception {
 573  0
             keyedFactory.destroyObject(key, obj);
 574  0
         }
 575  
 
 576  
         public boolean validateObject(final Object obj) {
 577  0
             return keyedFactory.validateObject(key, obj);
 578  
         }
 579  
 
 580  
         public void activateObject(final Object obj) throws Exception {
 581  0
             keyedFactory.activateObject(key, obj);
 582  0
         }
 583  
 
 584  
         public void passivateObject(final Object obj) throws Exception {
 585  0
             keyedFactory.passivateObject(key, obj);
 586  0
         }
 587  
 
 588  
         public String toString() {
 589  0
             final StringBuffer sb = new StringBuffer();
 590  0
             sb.append("PoolableObjectFactoryAdaptor");
 591  0
             sb.append("{key=").append(key);
 592  0
             sb.append(", keyedFactory=").append(keyedFactory);
 593  0
             sb.append('}');
 594  0
             return sb.toString();
 595  
         }
 596  
     }
 597  
 
 598  
     private static class KeyedPoolableObjectFactoryAdaptor implements KeyedPoolableObjectFactory {
 599  
         private final PoolableObjectFactory factory;
 600  
 
 601  0
         KeyedPoolableObjectFactoryAdaptor(final PoolableObjectFactory factory) throws IllegalArgumentException {
 602  0
             if (factory == null) {
 603  0
                 throw new IllegalArgumentException("factory must not be null.");
 604  
             }
 605  0
             this.factory = factory;
 606  0
         }
 607  
 
 608  
         public Object makeObject(final Object key) throws Exception {
 609  0
             return factory.makeObject();
 610  
         }
 611  
 
 612  
         public void destroyObject(final Object key, final Object obj) throws Exception {
 613  0
             factory.destroyObject(obj);
 614  0
         }
 615  
 
 616  
         public boolean validateObject(final Object key, final Object obj) {
 617  0
             return factory.validateObject(obj);
 618  
         }
 619  
 
 620  
         public void activateObject(final Object key, final Object obj) throws Exception {
 621  0
             factory.activateObject(obj);
 622  0
         }
 623  
 
 624  
         public void passivateObject(final Object key, final Object obj) throws Exception {
 625  0
             factory.passivateObject(obj);
 626  0
         }
 627  
 
 628  
         public String toString() {
 629  0
             final StringBuffer sb = new StringBuffer();
 630  0
             sb.append("KeyedPoolableObjectFactoryAdaptor");
 631  0
             sb.append("{factory=").append(factory);
 632  0
             sb.append('}');
 633  0
             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  0
         ObjectPoolAdaptor(final KeyedObjectPool keyedPool, final Object key) throws IllegalArgumentException {
 642  0
             if (keyedPool == null) {
 643  0
                 throw new IllegalArgumentException("keyedPool must not be null.");
 644  
             }
 645  0
             if (key == null) {
 646  0
                 throw new IllegalArgumentException("key must not be null.");
 647  
             }
 648  0
             this.keyedPool = keyedPool;
 649  0
             this.key = key;
 650  0
         }
 651  
 
 652  
         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
 653  0
             return keyedPool.borrowObject(key);
 654  
         }
 655  
 
 656  
         public void returnObject(final Object obj) {
 657  
             try {
 658  0
                 keyedPool.returnObject(key, obj);
 659  0
             } catch (Exception e) {
 660  
                 // swallowed as of Pool 2
 661  0
             }
 662  0
         }
 663  
 
 664  
         public void invalidateObject(final Object obj) {
 665  
             try {
 666  0
                 keyedPool.invalidateObject(key, obj);
 667  0
             } catch (Exception e) {
 668  
                 // swallowed as of Pool 2
 669  0
             }
 670  0
         }
 671  
 
 672  
         public void addObject() throws Exception, IllegalStateException {
 673  0
             keyedPool.addObject(key);
 674  0
         }
 675  
 
 676  
         public int getNumIdle() throws UnsupportedOperationException {
 677  0
             return keyedPool.getNumIdle(key);
 678  
         }
 679  
 
 680  
         public int getNumActive() throws UnsupportedOperationException {
 681  0
             return keyedPool.getNumActive(key);
 682  
         }
 683  
 
 684  
         public void clear() throws Exception, UnsupportedOperationException {
 685  0
             keyedPool.clear();
 686  0
         }
 687  
 
 688  
         public void close() {
 689  
             try {
 690  0
                 keyedPool.close();
 691  0
             } catch (Exception e) {
 692  
                 // swallowed as of Pool 2
 693  0
             }
 694  0
         }
 695  
 
 696  
         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 697  0
             keyedPool.setFactory(adapt(factory));
 698  0
         }
 699  
 
 700  
         public String toString() {
 701  0
             final StringBuffer sb = new StringBuffer();
 702  0
             sb.append("ObjectPoolAdaptor");
 703  0
             sb.append("{key=").append(key);
 704  0
             sb.append(", keyedPool=").append(keyedPool);
 705  0
             sb.append('}');
 706  0
             return sb.toString();
 707  
         }
 708  
     }
 709  
 
 710  
     private static class KeyedObjectPoolAdaptor implements KeyedObjectPool {
 711  
         private final ObjectPool pool;
 712  
 
 713  0
         KeyedObjectPoolAdaptor(final ObjectPool pool) throws IllegalArgumentException {
 714  0
             if (pool == null) {
 715  0
                 throw new IllegalArgumentException("pool must not be null.");
 716  
             }
 717  0
             this.pool = pool;
 718  0
         }
 719  
 
 720  
         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
 721  0
             return pool.borrowObject();
 722  
         }
 723  
 
 724  
         public void returnObject(final Object key, final Object obj) {
 725  
             try {
 726  0
                 pool.returnObject(obj);
 727  0
             } catch (Exception e) {
 728  
                 // swallowed as of Pool 2
 729  0
             }
 730  0
         }
 731  
 
 732  
         public void invalidateObject(final Object key, final Object obj) {
 733  
             try {
 734  0
                 pool.invalidateObject(obj);
 735  0
             } catch (Exception e) {
 736  
                 // swallowed as of Pool 2
 737  0
             }
 738  0
         }
 739  
 
 740  
         public void addObject(final Object key) throws Exception, IllegalStateException {
 741  0
             pool.addObject();
 742  0
         }
 743  
 
 744  
         public int getNumIdle(final Object key) throws UnsupportedOperationException {
 745  0
             return pool.getNumIdle();
 746  
         }
 747  
 
 748  
         public int getNumActive(final Object key) throws UnsupportedOperationException {
 749  0
             return pool.getNumActive();
 750  
         }
 751  
 
 752  
         public int getNumIdle() throws UnsupportedOperationException {
 753  0
             return pool.getNumIdle();
 754  
         }
 755  
 
 756  
         public int getNumActive() throws UnsupportedOperationException {
 757  0
             return pool.getNumActive();
 758  
         }
 759  
 
 760  
         public void clear() throws Exception, UnsupportedOperationException {
 761  0
             pool.clear();
 762  0
         }
 763  
 
 764  
         public void clear(final Object key) throws Exception, UnsupportedOperationException {
 765  0
             pool.clear();
 766  0
         }
 767  
 
 768  
         public void close() {
 769  
             try {
 770  0
                 pool.close();
 771  0
             } catch (Exception e) {
 772  
                 // swallowed as of Pool 2
 773  0
             }
 774  0
         }
 775  
 
 776  
         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 777  0
             pool.setFactory(adapt(factory));
 778  0
         }
 779  
 
 780  
         public String toString() {
 781  0
             final StringBuffer sb = new StringBuffer();
 782  0
             sb.append("KeyedObjectPoolAdaptor");
 783  0
             sb.append("{pool=").append(pool);
 784  0
             sb.append('}');
 785  0
             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  0
         CheckedObjectPool(final ObjectPool pool, final Class type) {
 794  0
             if (pool == null) {
 795  0
                 throw new IllegalArgumentException("pool must not be null.");
 796  
             }
 797  0
             if (type == null) {
 798  0
                 throw new IllegalArgumentException("type must not be null.");
 799  
             }
 800  0
             this.pool = pool;
 801  0
             this.type = type;
 802  0
         }
 803  
 
 804  
         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
 805  0
             final Object obj = pool.borrowObject();
 806  0
             if (type.isInstance(obj)) {
 807  0
                 return obj;
 808  
             } else {
 809  0
                 throw new ClassCastException("Borrowed object is not of type: " + type.getName() + " was: " + obj);
 810  
             }
 811  
         }
 812  
 
 813  
         public void returnObject(final Object obj) {
 814  0
             if (type.isInstance(obj)) {
 815  
                 try {
 816  0
                     pool.returnObject(obj);
 817  0
                 } catch (Exception e) {
 818  
                     // swallowed as of Pool 2
 819  0
                 }
 820  
             } else {
 821  0
                 throw new ClassCastException("Returned object is not of type: " + type.getName() + " was: " + obj);
 822  
             }
 823  0
         }
 824  
 
 825  
         public void invalidateObject(final Object obj) {
 826  0
             if (type.isInstance(obj)) {
 827  
                 try {
 828  0
                     pool.invalidateObject(obj);
 829  0
                 } catch (Exception e) {
 830  
                     // swallowed as of Pool 2
 831  0
                 }
 832  
             } else {
 833  0
                 throw new ClassCastException("Invalidated object is not of type: " + type.getName() + " was: " + obj);
 834  
             }
 835  0
         }
 836  
 
 837  
         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
 838  0
             pool.addObject();
 839  0
         }
 840  
 
 841  
         public int getNumIdle() throws UnsupportedOperationException {
 842  0
             return pool.getNumIdle();
 843  
         }
 844  
 
 845  
         public int getNumActive() throws UnsupportedOperationException {
 846  0
             return pool.getNumActive();
 847  
         }
 848  
 
 849  
         public void clear() throws Exception, UnsupportedOperationException {
 850  0
             pool.clear();
 851  0
         }
 852  
 
 853  
         public void close() {
 854  
             try {
 855  0
                 pool.close();
 856  0
             } catch (Exception e) {
 857  
                 // swallowed as of Pool 2
 858  0
             }
 859  0
         }
 860  
 
 861  
         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 862  0
             pool.setFactory(factory);
 863  0
         }
 864  
 
 865  
         public String toString() {
 866  0
             final StringBuffer sb = new StringBuffer();
 867  0
             sb.append("CheckedObjectPool");
 868  0
             sb.append("{type=").append(type);
 869  0
             sb.append(", pool=").append(pool);
 870  0
             sb.append('}');
 871  0
             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  0
         CheckedKeyedObjectPool(final KeyedObjectPool keyedPool, final Class type) {
 880  0
             if (keyedPool == null) {
 881  0
                 throw new IllegalArgumentException("keyedPool must not be null.");
 882  
             }
 883  0
             if (type == null) {
 884  0
                 throw new IllegalArgumentException("type must not be null.");
 885  
             }
 886  0
             this.keyedPool = keyedPool;
 887  0
             this.type = type;
 888  0
         }
 889  
 
 890  
         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
 891  0
             Object obj = keyedPool.borrowObject(key);
 892  0
             if (type.isInstance(obj)) {
 893  0
                 return obj;
 894  
             } else {
 895  0
                 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  0
             if (type.isInstance(obj)) {
 901  
                 try {
 902  0
                     keyedPool.returnObject(key, obj);
 903  0
                 } catch (Exception e) {
 904  
                     // swallowed as of Pool 2
 905  0
                 }
 906  
             } else {
 907  0
                 throw new ClassCastException("Returned object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
 908  
             }
 909  0
         }
 910  
 
 911  
         public void invalidateObject(final Object key, final Object obj) {
 912  0
             if (type.isInstance(obj)) {
 913  
                 try {
 914  0
                     keyedPool.invalidateObject(key, obj);
 915  0
                 } catch (Exception e) {
 916  
                     // swallowed as of Pool 2
 917  0
                 }
 918  
             } else {
 919  0
                 throw new ClassCastException("Invalidated object for key: " + key + " is not of type: " + type.getName() + " was: " + obj);
 920  
             }
 921  0
         }
 922  
 
 923  
         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
 924  0
             keyedPool.addObject(key);
 925  0
         }
 926  
 
 927  
         public int getNumIdle(final Object key) throws UnsupportedOperationException {
 928  0
             return keyedPool.getNumIdle(key);
 929  
         }
 930  
 
 931  
         public int getNumActive(final Object key) throws UnsupportedOperationException {
 932  0
             return keyedPool.getNumActive(key);
 933  
         }
 934  
 
 935  
         public int getNumIdle() throws UnsupportedOperationException {
 936  0
             return keyedPool.getNumIdle();
 937  
         }
 938  
 
 939  
         public int getNumActive() throws UnsupportedOperationException {
 940  0
             return keyedPool.getNumActive();
 941  
         }
 942  
 
 943  
         public void clear() throws Exception, UnsupportedOperationException {
 944  0
             keyedPool.clear();
 945  0
         }
 946  
 
 947  
         public void clear(final Object key) throws Exception, UnsupportedOperationException {
 948  0
             keyedPool.clear(key);
 949  0
         }
 950  
 
 951  
         public void close() {
 952  
             try {
 953  0
                 keyedPool.close();
 954  0
             } catch (Exception e) {
 955  
                 // swallowed as of Pool 2
 956  0
             }
 957  0
         }
 958  
 
 959  
         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 960  0
             keyedPool.setFactory(factory);
 961  0
         }
 962  
 
 963  
         public String toString() {
 964  0
             final StringBuffer sb = new StringBuffer();
 965  0
             sb.append("CheckedKeyedObjectPool");
 966  0
             sb.append("{type=").append(type);
 967  0
             sb.append(", keyedPool=").append(keyedPool);
 968  0
             sb.append('}');
 969  0
             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  0
         ObjectPoolMinIdleTimerTask(final ObjectPool pool, final int minIdle) throws IllegalArgumentException {
 978  0
             if (pool == null) {
 979  0
                 throw new IllegalArgumentException("pool must not be null.");
 980  
             }
 981  0
             this.pool = pool;
 982  0
             this.minIdle = minIdle;
 983  0
         }
 984  
 
 985  
         public void run() {
 986  0
             boolean success = false;
 987  
             try {
 988  0
                 if (pool.getNumIdle() < minIdle) {
 989  0
                     pool.addObject();
 990  
                 }
 991  0
                 success = true;
 992  
 
 993  0
             } catch (Exception e) {
 994  0
                 cancel();
 995  
 
 996  
             } finally {
 997  
                 // detect other types of Throwable and cancel this Timer
 998  0
                 if (!success) {
 999  0
                     cancel();
 1000  
                 }
 1001  
             }
 1002  0
         }
 1003  
 
 1004  
         public String toString() {
 1005  0
             final StringBuffer sb = new StringBuffer();
 1006  0
             sb.append("ObjectPoolMinIdleTimerTask");
 1007  0
             sb.append("{minIdle=").append(minIdle);
 1008  0
             sb.append(", pool=").append(pool);
 1009  0
             sb.append('}');
 1010  0
             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  0
         KeyedObjectPoolMinIdleTimerTask(final KeyedObjectPool keyedPool, final Object key, final int minIdle) throws IllegalArgumentException {
 1020  0
             if (keyedPool == null) {
 1021  0
                 throw new IllegalArgumentException("keyedPool must not be null.");
 1022  
             }
 1023  0
             this.keyedPool = keyedPool;
 1024  0
             this.key = key;
 1025  0
             this.minIdle = minIdle;
 1026  0
         }
 1027  
 
 1028  
         public void run() {
 1029  0
             boolean success = false;
 1030  
             try {
 1031  0
                 if (keyedPool.getNumIdle(key) < minIdle) {
 1032  0
                     keyedPool.addObject(key);
 1033  
                 }
 1034  0
                 success = true;
 1035  
 
 1036  0
             } catch (Exception e) {
 1037  0
                 cancel();
 1038  
 
 1039  
             } finally {
 1040  
                 // detect other types of Throwable and cancel this Timer
 1041  0
                 if (!success) {
 1042  0
                     cancel();
 1043  
                 }
 1044  
             }
 1045  0
         }
 1046  
 
 1047  
         public String toString() {
 1048  0
             final StringBuffer sb = new StringBuffer();
 1049  0
             sb.append("KeyedObjectPoolMinIdleTimerTask");
 1050  0
             sb.append("{minIdle=").append(minIdle);
 1051  0
             sb.append(", key=").append(key);
 1052  0
             sb.append(", keyedPool=").append(keyedPool);
 1053  0
             sb.append('}');
 1054  0
             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  0
         SynchronizedObjectPool(final ObjectPool pool) throws IllegalArgumentException {
 1063  0
             if (pool == null) {
 1064  0
                 throw new IllegalArgumentException("pool must not be null.");
 1065  
             }
 1066  0
             this.pool = pool;
 1067  0
             lock = new Object();
 1068  0
         }
 1069  
 
 1070  
         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
 1071  0
             synchronized (lock) {
 1072  0
                 return pool.borrowObject();
 1073  0
             }
 1074  
         }
 1075  
 
 1076  
         public void returnObject(final Object obj) {
 1077  0
             synchronized (lock) {
 1078  
                 try {
 1079  0
                     pool.returnObject(obj);
 1080  0
                 } catch (Exception e) {
 1081  
                     // swallowed as of Pool 2
 1082  0
                 }
 1083  0
             }
 1084  0
         }
 1085  
 
 1086  
         public void invalidateObject(final Object obj) {
 1087  0
             synchronized (lock) {
 1088  
                 try {
 1089  0
                     pool.invalidateObject(obj);
 1090  0
                 } catch (Exception e) {
 1091  
                     // swallowed as of Pool 2
 1092  0
                 }
 1093  0
             }
 1094  0
         }
 1095  
 
 1096  
         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
 1097  0
             synchronized (lock) {
 1098  0
                 pool.addObject();
 1099  0
             }
 1100  0
         }
 1101  
 
 1102  
         public int getNumIdle() throws UnsupportedOperationException {
 1103  0
             synchronized (lock) {
 1104  0
                 return pool.getNumIdle();
 1105  0
             }
 1106  
         }
 1107  
 
 1108  
         public int getNumActive() throws UnsupportedOperationException {
 1109  0
             synchronized (lock) {
 1110  0
                 return pool.getNumActive();
 1111  0
             }
 1112  
         }
 1113  
 
 1114  
         public void clear() throws Exception, UnsupportedOperationException {
 1115  0
             synchronized (lock) {
 1116  0
                 pool.clear();
 1117  0
             }
 1118  0
         }
 1119  
 
 1120  
         public void close() {
 1121  
             try {
 1122  0
                 synchronized (lock) {
 1123  0
                     pool.close();
 1124  0
                 }
 1125  0
             } catch (Exception e) {
 1126  
                 // swallowed as of Pool 2
 1127  0
             }
 1128  0
         }
 1129  
 
 1130  
         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 1131  0
             synchronized (lock) {
 1132  0
                 pool.setFactory(factory);
 1133  0
             }
 1134  0
         }
 1135  
 
 1136  
         public String toString() {
 1137  0
             final StringBuffer sb = new StringBuffer();
 1138  0
             sb.append("SynchronizedObjectPool");
 1139  0
             sb.append("{pool=").append(pool);
 1140  0
             sb.append('}');
 1141  0
             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  0
         SynchronizedKeyedObjectPool(final KeyedObjectPool keyedPool) throws IllegalArgumentException {
 1150  0
             if (keyedPool == null) {
 1151  0
                 throw new IllegalArgumentException("keyedPool must not be null.");
 1152  
             }
 1153  0
             this.keyedPool = keyedPool;
 1154  0
             lock = new Object();
 1155  0
         }
 1156  
 
 1157  
         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
 1158  0
             synchronized (lock) {
 1159  0
                 return keyedPool.borrowObject(key);
 1160  0
             }
 1161  
         }
 1162  
 
 1163  
         public void returnObject(final Object key, final Object obj) {
 1164  0
             synchronized (lock) {
 1165  
                 try {
 1166  0
                     keyedPool.returnObject(key, obj);
 1167  0
                 } catch (Exception e) {
 1168  
                     // swallowed
 1169  0
                 }
 1170  0
             }
 1171  0
         }
 1172  
 
 1173  
         public void invalidateObject(final Object key, final Object obj) {
 1174  0
             synchronized (lock) {
 1175  
                 try {
 1176  0
                     keyedPool.invalidateObject(key, obj);
 1177  0
                 } catch (Exception e) {
 1178  
                     // swallowed as of Pool 2
 1179  0
                 }
 1180  0
             }
 1181  0
         }
 1182  
 
 1183  
         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
 1184  0
             synchronized (lock) {
 1185  0
                 keyedPool.addObject(key);
 1186  0
             }
 1187  0
         }
 1188  
 
 1189  
         public int getNumIdle(final Object key) throws UnsupportedOperationException {
 1190  0
             synchronized (lock) {
 1191  0
                 return keyedPool.getNumIdle(key);
 1192  0
             }
 1193  
         }
 1194  
 
 1195  
         public int getNumActive(final Object key) throws UnsupportedOperationException {
 1196  0
             synchronized (lock) {
 1197  0
                 return keyedPool.getNumActive(key);
 1198  0
             }
 1199  
         }
 1200  
 
 1201  
         public int getNumIdle() throws UnsupportedOperationException {
 1202  0
             synchronized (lock) {
 1203  0
                 return keyedPool.getNumIdle();
 1204  0
             }
 1205  
         }
 1206  
 
 1207  
         public int getNumActive() throws UnsupportedOperationException {
 1208  0
             synchronized (lock) {
 1209  0
                 return keyedPool.getNumActive();
 1210  0
             }
 1211  
         }
 1212  
 
 1213  
         public void clear() throws Exception, UnsupportedOperationException {
 1214  0
             synchronized (lock) {
 1215  0
                 keyedPool.clear();
 1216  0
             }
 1217  0
         }
 1218  
 
 1219  
         public void clear(final Object key) throws Exception, UnsupportedOperationException {
 1220  0
             synchronized (lock) {
 1221  0
                 keyedPool.clear(key);
 1222  0
             }
 1223  0
         }
 1224  
 
 1225  
         public void close() {
 1226  
             try {
 1227  0
                 synchronized (lock) {
 1228  0
                     keyedPool.close();
 1229  0
                 }
 1230  0
             } catch (Exception e) {
 1231  
                 // swallowed as of Pool 2
 1232  0
             }
 1233  0
         }
 1234  
 
 1235  
         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 1236  0
             synchronized (lock) {
 1237  0
                 keyedPool.setFactory(factory);
 1238  0
             }
 1239  0
         }
 1240  
 
 1241  
         public String toString() {
 1242  0
             final StringBuffer sb = new StringBuffer();
 1243  0
             sb.append("SynchronizedKeyedObjectPool");
 1244  0
             sb.append("{keyedPool=").append(keyedPool);
 1245  0
             sb.append('}');
 1246  0
             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  0
         SynchronizedPoolableObjectFactory(final PoolableObjectFactory factory) throws IllegalArgumentException {
 1255  0
             if (factory == null) {
 1256  0
                 throw new IllegalArgumentException("factory must not be null.");
 1257  
             }
 1258  0
             this.factory = factory;
 1259  0
             lock = new Object();
 1260  0
         }
 1261  
 
 1262  
         public Object makeObject() throws Exception {
 1263  0
             synchronized (lock) {
 1264  0
                 return factory.makeObject();
 1265  0
             }
 1266  
         }
 1267  
 
 1268  
         public void destroyObject(final Object obj) throws Exception {
 1269  0
             synchronized (lock) {
 1270  0
                 factory.destroyObject(obj);
 1271  0
             }
 1272  0
         }
 1273  
 
 1274  
         public boolean validateObject(final Object obj) {
 1275  0
             synchronized (lock) {
 1276  0
                 return factory.validateObject(obj);
 1277  0
             }
 1278  
         }
 1279  
 
 1280  
         public void activateObject(final Object obj) throws Exception {
 1281  0
             synchronized (lock) {
 1282  0
                 factory.activateObject(obj);
 1283  0
             }
 1284  0
         }
 1285  
 
 1286  
         public void passivateObject(final Object obj) throws Exception {
 1287  0
             synchronized (lock) {
 1288  0
                 factory.passivateObject(obj);
 1289  0
             }
 1290  0
         }
 1291  
 
 1292  
         public String toString() {
 1293  0
             final StringBuffer sb = new StringBuffer();
 1294  0
             sb.append("SynchronizedPoolableObjectFactory");
 1295  0
             sb.append("{factory=").append(factory);
 1296  0
             sb.append('}');
 1297  0
             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  0
         SynchronizedKeyedPoolableObjectFactory(final KeyedPoolableObjectFactory keyedFactory) throws IllegalArgumentException {
 1306  0
             if (keyedFactory == null) {
 1307  0
                 throw new IllegalArgumentException("keyedFactory must not be null.");
 1308  
             }
 1309  0
             this.keyedFactory = keyedFactory;
 1310  0
             lock = new Object();
 1311  0
         }
 1312  
 
 1313  
         public Object makeObject(final Object key) throws Exception {
 1314  0
             synchronized (lock) {
 1315  0
                 return keyedFactory.makeObject(key);
 1316  0
             }
 1317  
         }
 1318  
 
 1319  
         public void destroyObject(final Object key, final Object obj) throws Exception {
 1320  0
             synchronized (lock) {
 1321  0
                 keyedFactory.destroyObject(key, obj);
 1322  0
             }
 1323  0
         }
 1324  
 
 1325  
         public boolean validateObject(final Object key, final Object obj) {
 1326  0
             synchronized (lock) {
 1327  0
                 return keyedFactory.validateObject(key, obj);
 1328  0
             }
 1329  
         }
 1330  
 
 1331  
         public void activateObject(final Object key, final Object obj) throws Exception {
 1332  0
             synchronized (lock) {
 1333  0
                 keyedFactory.activateObject(key, obj);
 1334  0
             }
 1335  0
         }
 1336  
 
 1337  
         public void passivateObject(final Object key, final Object obj) throws Exception {
 1338  0
             synchronized (lock) {
 1339  0
                 keyedFactory.passivateObject(key, obj);
 1340  0
             }
 1341  0
         }
 1342  
 
 1343  
         public String toString() {
 1344  0
             final StringBuffer sb = new StringBuffer();
 1345  0
             sb.append("SynchronizedKeyedPoolableObjectFactory");
 1346  0
             sb.append("{keyedFactory=").append(keyedFactory);
 1347  0
             sb.append('}');
 1348  0
             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  0
         public ErodingFactor(final float factor) {
 1361  0
             this.factor = factor;
 1362  0
             nextShrink = System.currentTimeMillis() + (long)(900000 * factor); // now + 15 min * factor
 1363  0
             idleHighWaterMark = 1;
 1364  0
         }
 1365  
 
 1366  
         public void update(final int numIdle) {
 1367  0
             update(System.currentTimeMillis(), numIdle);
 1368  0
         }
 1369  
 
 1370  
         public void update(final long now, final int numIdle) {
 1371  0
             final int idle = Math.max(0, numIdle);
 1372  0
             idleHighWaterMark = Math.max(idle, idleHighWaterMark);
 1373  0
             final float maxInterval = 15f;
 1374  0
             final float minutes = maxInterval + ((1f-maxInterval)/idleHighWaterMark) * idle;
 1375  0
             nextShrink = now + (long)(minutes * 60000f * factor);
 1376  0
         }
 1377  
 
 1378  
         public long getNextShrink() {
 1379  0
             return nextShrink;
 1380  
         }
 1381  
 
 1382  
         public String toString() {
 1383  0
             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  0
         public ErodingObjectPool(final ObjectPool pool, final float factor) {
 1395  0
             this.pool = pool;
 1396  0
             this.factor = new ErodingFactor(factor);
 1397  0
         }
 1398  
 
 1399  
         public Object borrowObject() throws Exception, NoSuchElementException, IllegalStateException {
 1400  0
             return pool.borrowObject();
 1401  
         }
 1402  
 
 1403  
         public void returnObject(final Object obj) {
 1404  0
             boolean discard = false;
 1405  0
             final long now = System.currentTimeMillis();
 1406  0
             synchronized (pool) {
 1407  0
                 if (factor.getNextShrink() < now) { // XXX: Pool 3: move test out of sync block
 1408  0
                     final int numIdle = pool.getNumIdle();
 1409  0
                     if (numIdle > 0) {
 1410  0
                         discard = true;
 1411  
                     }
 1412  
 
 1413  0
                     factor.update(now, numIdle);
 1414  
                 }
 1415  0
             }
 1416  
             try {
 1417  0
                 if (discard) {
 1418  0
                     pool.invalidateObject(obj);
 1419  
                 } else {
 1420  0
                     pool.returnObject(obj);
 1421  
                 }
 1422  0
             } catch (Exception e) {
 1423  
                 // swallowed
 1424  0
             }
 1425  0
         }
 1426  
 
 1427  
         public void invalidateObject(final Object obj) {
 1428  
             try {
 1429  0
                 pool.invalidateObject(obj);
 1430  0
             } catch (Exception e) {
 1431  
                 // swallowed
 1432  0
             }
 1433  0
         }
 1434  
 
 1435  
         public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
 1436  0
             pool.addObject();
 1437  0
         }
 1438  
 
 1439  
         public int getNumIdle() throws UnsupportedOperationException {
 1440  0
             return pool.getNumIdle();
 1441  
         }
 1442  
 
 1443  
         public int getNumActive() throws UnsupportedOperationException {
 1444  0
             return pool.getNumActive();
 1445  
         }
 1446  
 
 1447  
         public void clear() throws Exception, UnsupportedOperationException {
 1448  0
             pool.clear();
 1449  0
         }
 1450  
 
 1451  
         public void close() {
 1452  
             try {
 1453  0
                 pool.close();
 1454  0
             } catch (Exception e) {
 1455  
                 // swallowed
 1456  0
             }
 1457  0
         }
 1458  
 
 1459  
         public void setFactory(final PoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 1460  0
             pool.setFactory(factory);
 1461  0
         }
 1462  
 
 1463  
         public String toString() {
 1464  0
             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  0
             this(keyedPool, new ErodingFactor(factor));
 1477  0
         }
 1478  
 
 1479  0
         protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final ErodingFactor erodingFactor) {
 1480  0
             if (keyedPool == null) {
 1481  0
                 throw new IllegalArgumentException("keyedPool must not be null.");
 1482  
             }
 1483  0
             this.keyedPool = keyedPool;
 1484  0
             this.erodingFactor = erodingFactor;
 1485  0
         }
 1486  
 
 1487  
         public Object borrowObject(final Object key) throws Exception, NoSuchElementException, IllegalStateException {
 1488  0
             return keyedPool.borrowObject(key);
 1489  
         }
 1490  
 
 1491  
         public void returnObject(final Object key, final Object obj) throws Exception {
 1492  0
             boolean discard = false;
 1493  0
             final long now = System.currentTimeMillis();
 1494  0
             final ErodingFactor factor = getErodingFactor(key);
 1495  0
             synchronized (keyedPool) {
 1496  0
                 if (factor.getNextShrink() < now) {
 1497  0
                     final int numIdle = numIdle(key);
 1498  0
                     if (numIdle > 0) {
 1499  0
                         discard = true;
 1500  
                     }
 1501  
 
 1502  0
                     factor.update(now, numIdle);
 1503  
                 }
 1504  0
             }
 1505  
             try {
 1506  0
                 if (discard) {
 1507  0
                     keyedPool.invalidateObject(key, obj);
 1508  
                 } else {
 1509  0
                     keyedPool.returnObject(key, obj);
 1510  
                 }
 1511  0
             } catch (Exception e) {
 1512  
                 // swallowed
 1513  0
             }
 1514  0
         }
 1515  
 
 1516  
         protected int numIdle(final Object key) {
 1517  0
             return getKeyedPool().getNumIdle();
 1518  
         }
 1519  
 
 1520  
         protected ErodingFactor getErodingFactor(final Object key) {
 1521  0
             return erodingFactor;
 1522  
         }
 1523  
 
 1524  
         public void invalidateObject(final Object key, final Object obj) {
 1525  
             try {
 1526  0
                 keyedPool.invalidateObject(key, obj);
 1527  0
             } catch (Exception e) {
 1528  
                 // swallowed
 1529  0
             }
 1530  0
         }
 1531  
 
 1532  
         public void addObject(final Object key) throws Exception, IllegalStateException, UnsupportedOperationException {
 1533  0
             keyedPool.addObject(key);
 1534  0
         }
 1535  
 
 1536  
         public int getNumIdle() throws UnsupportedOperationException {
 1537  0
             return keyedPool.getNumIdle();
 1538  
         }
 1539  
 
 1540  
         public int getNumIdle(final Object key) throws UnsupportedOperationException {
 1541  0
             return keyedPool.getNumIdle(key);
 1542  
         }
 1543  
 
 1544  
         public int getNumActive() throws UnsupportedOperationException {
 1545  0
             return keyedPool.getNumActive();
 1546  
         }
 1547  
 
 1548  
         public int getNumActive(final Object key) throws UnsupportedOperationException {
 1549  0
             return keyedPool.getNumActive(key);
 1550  
         }
 1551  
 
 1552  
         public void clear() throws Exception, UnsupportedOperationException {
 1553  0
             keyedPool.clear();
 1554  0
         }
 1555  
 
 1556  
         public void clear(final Object key) throws Exception, UnsupportedOperationException {
 1557  0
             keyedPool.clear(key);
 1558  0
         }
 1559  
 
 1560  
         public void close() {
 1561  
             try {
 1562  0
                 keyedPool.close();
 1563  0
             } catch (Exception e) {
 1564  
                 // swallowed
 1565  0
             }
 1566  0
         }
 1567  
 
 1568  
         public void setFactory(final KeyedPoolableObjectFactory factory) throws IllegalStateException, UnsupportedOperationException {
 1569  0
             keyedPool.setFactory(factory);
 1570  0
         }
 1571  
 
 1572  
         protected KeyedObjectPool getKeyedPool() {
 1573  0
             return keyedPool;
 1574  
         }
 1575  
 
 1576  
         public String toString() {
 1577  0
             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  0
         private final Map factors = Collections.synchronizedMap(new HashMap());
 1587  
 
 1588  
         public ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) {
 1589  0
             super(keyedPool, null);
 1590  0
             this.factor = factor;
 1591  0
         }
 1592  
 
 1593  
         protected int numIdle(final Object key) {
 1594  0
             return getKeyedPool().getNumIdle(key);
 1595  
         }
 1596  
 
 1597  
         protected ErodingFactor getErodingFactor(final Object key) {
 1598  0
             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  0
             if (factor == null) {
 1602  0
                 factor = new ErodingFactor(this.factor);
 1603  0
                 factors.put(key, factor);
 1604  
             }
 1605  0
             return factor;
 1606  
         }
 1607  
 
 1608  
         public String toString() {
 1609  0
             return "ErodingPerKeyKeyedObjectPool{" +
 1610  
                     "factor=" + factor +
 1611  
                     ", keyedPool=" + getKeyedPool() +
 1612  
                     '}';
 1613  
         }
 1614  
     }
 1615  
 }