org.apache.commons.pool.impl.FairGenericObjectPool.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.commons.pool.impl.FairGenericObjectPool.java

Source

/* FairGenericObjectPool
*
* $Id: FairGenericObjectPool.java 4672 2006-09-27 00:03:16Z paul_jack $
*
* Created on Apr 7, 2006
*
* Copyright (C) 2006 Internet Archive.
*/

package org.apache.commons.pool.impl;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;

import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPool.ObjectTimestampPair;

/**
 * Version of GenericObjectPool which is 'fair' with respect to the client
 * threads using {@link #borrowObject <i>borrowObject</i>}. Those which enter
 * first will receive objects from the pool first. 
 * 
 * 
 * @see GenericObjectPool
 * @author Gordon Mohr
 * @version $Revision: 4672 $ $Date: 2006-09-27 00:03:16 +0000 (Wed, 27 Sep 2006) $
 */
@SuppressWarnings("unchecked")
public class FairGenericObjectPool extends GenericObjectPool {

    //--- constructors -----------------------------------------------
    // (all copied from superclass; only last adds one additional line of
    // initialization and call to superclass)

    /**
     * Create a new <tt>FairGenericObjectPool</tt>.
     */
    public FairGenericObjectPool() {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     */
    public FairGenericObjectPool(PoolableObjectFactory factory) {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param config a non-<tt>null</tt> {@link GenericObjectPool.Config} describing my configuration
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) {
        this(factory, config.maxActive, config.whenExhaustedAction, config.maxWait, config.maxIdle, config.minIdle,
                config.testOnBorrow, config.testOnReturn, config.timeBetweenEvictionRunsMillis,
                config.numTestsPerEvictionRun, config.minEvictableIdleTimeMillis, config.testWhileIdle);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive) {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
     * @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})
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait) {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
     * @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})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #getTestOnReturn})
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, boolean testOnBorrow, boolean testOnReturn) {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
     * @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})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #getMaxIdle})
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle) {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #getWhenExhaustedAction})
     * @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})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #getMaxIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #getTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #getTestOnReturn})
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn) {
        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);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @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})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
     */
    public FairGenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction,
            long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn,
            long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
            boolean testWhileIdle) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, DEFAULT_MIN_IDLE, testOnBorrow,
                testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis,
                testWhileIdle);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @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})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
     */
    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) {
        this(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn,
                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
                DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS);
    }

    /**
     * Create a new <tt>FairGenericObjectPool</tt> using the specified values.
     * @param factory the (possibly <tt>null</tt>)PoolableObjectFactory to use to create, validate and destroy objects
     * @param maxActive the maximum number of objects that can be borrowed from me at one time (see {@link #setMaxActive})
     * @param whenExhaustedAction the action to take when the pool is exhausted (see {@link #setWhenExhaustedAction})
     * @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})
     * @param maxIdle the maximum number of idle objects in my pool (see {@link #setMaxIdle})
     * @param minIdle the minimum number of idle objects in my pool (see {@link #setMinIdle})
     * @param testOnBorrow whether or not to validate objects before they are returned by the {@link #borrowObject} method (see {@link #setTestOnBorrow})
     * @param testOnReturn whether or not to validate objects after they are returned to the {@link #returnObject} method (see {@link #setTestOnReturn})
     * @param timeBetweenEvictionRunsMillis the amount of time (in milliseconds) to sleep between examining idle objects for eviction (see {@link #setTimeBetweenEvictionRunsMillis})
     * @param numTestsPerEvictionRun the number of idle objects to examine per run within the idle object eviction thread (if any) (see {@link #setNumTestsPerEvictionRun})
     * @param minEvictableIdleTimeMillis the minimum number of milliseconds an object can sit idle in the pool before it is eligable for evcition (see {@link #setMinEvictableIdleTimeMillis})
     * @param testWhileIdle whether or not to validate objects in the idle object eviction thread, if any (see {@link #setTestWhileIdle})
     * @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})
     */
    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) {
        super(factory, maxActive, whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn,
                timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
                softMinEvictableIdleTimeMillis);
        _borrowerQueue = Collections.synchronizedList(new LinkedList());
    }

    //-- ObjectPool methods ------------------------------------------

    /** 
     * 
     * @see org.apache.commons.pool.ObjectPool#borrowObject()
     */
    public Object borrowObject() throws Exception {
        assertOpen();
        long starttime = System.currentTimeMillis();

        try {
            synchronized (this) {
                // use borrowerQueue
                _borrowerQueue.add(Thread.currentThread());

                for (;;) {
                    ObjectTimestampPair pair = null;

                    // Only allow current thread to receive pool object if
                    // thread is top of queue 
                    boolean eligible = _borrowerQueue.get(0) == Thread.currentThread();
                    if (eligible) {
                        // if there are any sleeping, just grab one of those
                        try {
                            pair = (ObjectTimestampPair) (_pool.removeFirst());
                        } catch (NoSuchElementException e) {
                            ; /* ignored */
                        }
                    }

                    // otherwise
                    if (null == pair) {
                        // check if we can create one
                        // (note we know that the num sleeping is 0, else we wouldn't be here)
                        if (eligible && (_maxActive < 0 || _numActive < _maxActive)) {
                            // allow new object to be created
                        } else {
                            // the pool is exhausted
                            // or current thread is ineligible due to fairness
                            switch (_whenExhaustedAction) {
                            case WHEN_EXHAUSTED_GROW:
                                // allow new object to be created
                                break;
                            case WHEN_EXHAUSTED_FAIL:
                                throw new NoSuchElementException("Pool exhausted");
                            case WHEN_EXHAUSTED_BLOCK:
                                try {
                                    if (_maxWait <= 0) {
                                        wait();
                                    } else {
                                        // this code may be executed again after a notify then continue cycle
                                        // so, need to calculate the amount of time to wait
                                        final long elapsed = (System.currentTimeMillis() - starttime);
                                        final long waitTime = _maxWait - elapsed;
                                        if (waitTime > 0) {
                                            wait(waitTime);
                                        }
                                    }
                                } catch (InterruptedException e) {
                                    // ignored
                                }
                                if (_maxWait > 0 && ((System.currentTimeMillis() - starttime) >= _maxWait)) {
                                    throw new NoSuchElementException("Timeout waiting for idle object");
                                } else {
                                    continue; // keep looping
                                }
                            default:
                                throw new IllegalArgumentException("WhenExhaustedAction property "
                                        + _whenExhaustedAction + " not recognized.");
                            }
                        }
                    }
                    _numActive++;

                    // create new object when needed
                    boolean newlyCreated = false;
                    if (null == pair) {
                        try {
                            Object obj = _factory.makeObject();
                            pair = new ObjectTimestampPair(obj);
                            newlyCreated = true;
                            return pair.value;
                        } finally {
                            if (!newlyCreated) {
                                // object cannot be created
                                _numActive--;
                                notifyAll();
                            }
                        }
                    }

                    // activate & validate the object
                    try {
                        _factory.activateObject(pair.value);
                        if (_testOnBorrow && !_factory.validateObject(pair.value)) {
                            throw new Exception("ValidateObject failed");
                        }
                        return pair.value;
                    } catch (Throwable e) {
                        // object cannot be activated or is invalid
                        _numActive--;
                        notifyAll();
                        try {
                            _factory.destroyObject(pair.value);
                        } catch (Throwable e2) {
                            // cannot destroy broken object
                        }
                        if (newlyCreated) {
                            throw new NoSuchElementException(
                                    "Could not create a validated object, cause: " + e.getMessage());
                        } else {
                            continue; // keep looping
                        }
                    }
                }
            }
        } finally {
            // remove thread from queue on any method exit
            _borrowerQueue.remove(Thread.currentThread());
        }
    }

    /** Waiting borrowers (threads in #borrowObject ) */
    protected List _borrowerQueue;
}