    1   /* FairGenericObjectPool
    2   *
    3   * $Id: FairGenericObjectPool.java 4672 2006-09-27 00:03:16Z paul_jack $
    4   *
    5   * Created on Apr 7, 2006
    6   *
    7   * Copyright (C) 2006 Internet Archive.
    8   */
    9
    10  package org.apache.commons.pool.impl;
    11
    12  import java.util.Collections;
    13  import java.util.LinkedList;
    14  import java.util.List;
    15  import java.util.NoSuchElementException;
    16
    17  import org.apache.commons.pool.PoolableObjectFactory;
    18  import org.apache.commons.pool.impl.GenericKeyedObjectPool.ObjectTimestampPair;
    19
    20  /***
    21   * Version of GenericObjectPool which is 'fair' with respect to the client
    22   * threads using {@link #borrowObject <i>borrowObject</i>}. Those which enter
    23   * first will receive objects from the pool first.
    24   *
    25   *
    26   * @see GenericObjectPool
    27   * @author Gordon Mohr
    28   * @version $Revision: 4672 $ $Date: 2006-09-27 00:03:16 +0000 (Wed, 27 Sep 2006) $
    29   */
    30  @SuppressWarnings("unchecked")
    31  public class FairGenericObjectPool extends GenericObjectPool {
    32
    33      //--- constructors -----------------------------------------------
    34      // (all copied from superclass; only last adds one additional line of
    35      // initialization and call to superclass)
    36
    37      /***
    38       * Create a new <tt>FairGenericObjectPool</tt>.
    39       */
    40      public FairGenericObjectPool() {
    41          this(null,DEFAULT_MAX_ACTIVE,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_MIN_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    42      }
    43
    44      /***
    45       * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    46       * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    47       */
    48      public FairGenericObjectPool(PoolableObjectFactory factory) {
    49          this(factory,DEFAULT_MAX_ACTIVE,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_MIN_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    50      }
    51
    52      /***
    53       * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    54       * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    55       * @param config a non-<tt>null</tt> {@link GenericObjectPool.Config} describing my configuration
    56       */
    57      public FairGenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) {
    58          this(factory,config.maxActive,config.whenExhaustedAction,config.maxWait,config.maxIdle,config.minIdle,config.testOnBorrow,config.testOnReturn,config.timeBetweenEvictionRunsMillis,config.numTestsPerEvictionRun,config.minEvictableIdleTimeMillis,config.testWhileIdle);
    59      }
    60
    61      /***
    62       * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    63       * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    64       * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    65       */
    66      public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive) {
    67          this(factory,maxActive,DEFAULT_WHEN_EXHAUSTED_ACTION,DEFAULT_MAX_WAIT,DEFAULT_MAX_IDLE,DEFAULT_MIN_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    68      }
    69
    70      /***
    71       * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    72       * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    73       * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    74       * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
    75       * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
    76       */
    77      public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait) {
    78          this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,DEFAULT_MIN_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    79      }
    80
    81      /***
    82       * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    83       * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    84       * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    85       * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
    86       * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
    87       * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
    88       * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #getTestOnReturn})
    89       */
    90      public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn) {
    91          this(factory,maxActive,whenExhaustedAction,maxWait,DEFAULT_MAX_IDLE,DEFAULT_MIN_IDLE,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    92      }
    93
    94      /***
    95       * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    96       * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    97       * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    98       * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
    99       * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
    100      * @param maxIdle the maximum number of idle objects in my pool (see {@link #getMaxIdle})
    101      */
    102     public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle) {
    103         this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,DEFAULT_MIN_IDLE,DEFAULT_TEST_ON_BORROW,DEFAULT_TEST_ON_RETURN,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    104     }
    105
    106     /***
    107      * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    108      * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    109      * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    110      * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
    111      * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #getMaxWait})
    112      * @param maxIdle the maximum number of idle objects in my pool (see {@link #getMaxIdle})
    113      * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
    114      * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #getTestOnReturn})
    115      */
    116     public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
    117         this(factory,maxActive,whenExhaustedAction,maxWait,maxIdle,DEFAULT_MIN_IDLE,testOnBorrow,testOnReturn,DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,DEFAULT_NUM_TESTS_PER_EVICTION_RUN,DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,DEFAULT_TEST_WHILE_IDLE);
    118     }
    119
    120     /***
    121      * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    122      * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    123      * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    124      * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
    125      * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
    126      * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
    127      * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
    128      * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
    129      * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
    130      * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
    131      * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
    132      * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
    133      */
    134     public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
    135         this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_MIN_IDLE, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle);
    136     }
    137
    138     /***
    139      * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    140      * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    141      * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    142      * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
    143      * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
    144      * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
    145      * @param minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
    146      * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
    147      * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
    148      * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
    149      * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
    150      * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
    151      * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
    152      */
    153     public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle) {
    154         this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle, DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
    155     }
    156
    157     /***
    158      * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
    159      * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
    160      * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
    161      * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
    162      * @param maxWait the maximum amount of time to wait for an idle object when the pool is exhausted an and <i>whenExhaustedAction</i> is {@link #WHEN_EXHAUSTED_BLOCK} (otherwise ignored) (see {@link #setMaxWait})
    163      * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
    164      * @param minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
    165      * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
    166      * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
    167      * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
    168      * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
    169      * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
    170      * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
    171      * @param softMinEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition with the extra condition that at least "minIdle" amount of object remain in the pool. (see {@link #setSoftMinEvictableIdleTimeMillis})
    172      */
    173     public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, int minIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle, long softMinEvictableIdleTimeMillis) {
    174         super(factory, maxActive, whenExhaustedAction, maxWait, maxIdle,
    175                 minIdle, testOnBorrow, testOnReturn,
    176                 timeBetweenEvictionRunsMillis, numTestsPerEvictionRun,
    177                 minEvictableIdleTimeMillis, testWhileIdle,
    178                 softMinEvictableIdleTimeMillis);
    179         _borrowerQueue = Collections.synchronizedList(new LinkedList());
    180     }
    181
    182     //-- ObjectPool methods ------------------------------------------
    183
    184     /***
    185      *
    186      * @see org.apache.commons.pool.ObjectPool#borrowObject()
    187      */
    188     public Object borrowObject() throws Exception {
    189         assertOpen();
    190         long starttime = System.currentTimeMillis();
    191
    192
    193
    194         try {
    195             synchronized(this) {
    196                 // use borrowerQueue
    197                 _borrowerQueue.add(Thread.currentThread());
    198
    199                 for(;;) {
    200                     ObjectTimestampPair pair = null;
    201
    202                     // Only allow current thread to receive pool object if
    203                     // thread is top of queue
    204                     boolean eligible = _borrowerQueue.get(0)==Thread.currentThread();
    205                     if(eligible) {
    206                         // if there are any sleeping, just grab one of those
    207                         try {
    208                             pair = (ObjectTimestampPair)(_pool.removeFirst());
    209                         } catch(NoSuchElementException e) {
    210                             ; /* ignored */
    211                         }
    212                     }
    213
    214                     // otherwise
    215                     if(null == pair) {
    216                         // check if we can create one
    217                         // (note we know that the num sleeping is 0, else we wouldn't be here)
    218                         if(eligible && (_maxActive < 0 || _numActive < _maxActive)) {
    219                             // allow new object to be created
    220                         } else {
    221                             // the pool is exhausted
    222                             // or current thread is ineligible due to fairness
    223                             switch(_whenExhaustedAction) {
    224                                 case WHEN_EXHAUSTED_GROW:
    225                                     // allow new object to be created
    226                                     break;
    227                                 case WHEN_EXHAUSTED_FAIL:
    228                                     throw new NoSuchElementException("Pool exhausted");
    229                                 case WHEN_EXHAUSTED_BLOCK:
    230                                     try {
    231                                         if(_maxWait <= 0) {
    232                                             wait();
    233                                         } else {
    234                                             // this code may be executed again after a notify then continue cycle
    235                                             // so, need to calculate the amount of time to wait
    236                                             final long elapsed = (System.currentTimeMillis() - starttime);
    237                                             final long waitTime = _maxWait - elapsed;
    238                                             if (waitTime > 0)
    239                                             {
    240                                                 wait(waitTime);
    241                                             }
    242                                         }
    243                                     } catch(InterruptedException e) {
    244                                         // ignored
    245                                     }
    246                                     if(_maxWait > 0 && ((System.currentTimeMillis() - starttime) >= _maxWait)) {
    247                                         throw new NoSuchElementException("Timeout waiting for idle object");
    248                                     } else {
    249                                         continue; // keep looping
    250                                     }
    251                                 default:
    252                                     throw new IllegalArgumentException("WhenExhaustedAction property " + _whenExhaustedAction + " not recognized.");
    253                             }
    254                         }
    255                     }
    256                     _numActive++;
    257
    258                     // create new object when needed
    259                     boolean newlyCreated = false;
    260                     if(null == pair) {
    261                         try {
    262                             Object obj = _factory.makeObject();
    263                             pair = new ObjectTimestampPair(obj);
    264                             newlyCreated = true;
    265                             return pair.value;
    266                         } finally {
    267                             if (!newlyCreated) {
    268                                 // object cannot be created
    269                                 _numActive--;
    270                                 notifyAll();
    271                             }
    272                         }
    273                     }
    274
    275                     // activate & validate the object
    276                     try {
    277                         _factory.activateObject(pair.value);
    278                         if(_testOnBorrow && !_factory.validateObject(pair.value)) {
    279                             throw new Exception("ValidateObject failed");
    280                         }
    281                         return pair.value;
    282                     }
    283                     catch (Throwable e) {
    284                         // object cannot be activated or is invalid
    285                         _numActive--;
    286                         notifyAll();
    287                         try {
    288                             _factory.destroyObject(pair.value);
    289                         }
    290                         catch (Throwable e2) {
    291                             // cannot destroy broken object
    292                         }
    293                         if(newlyCreated) {
    294                             throw new NoSuchElementException("Could not create a validated object, cause: " + e.getMessage());
    295                         }
    296                         else {
    297                             continue; // keep looping
    298                         }
    299                     }
    300                 }
    301             }
    302         } finally {
    303             // remove thread from queue on any method exit
    304             _borrowerQueue.remove(Thread.currentThread());
    305         }
    306     }
    307
    308     /*** Waiting borrowers (threads in #borrowObject ) */
    309     protected List _borrowerQueue;
    310 }