This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-pool.git
The following commit(s) were added to refs/heads/master by this push: new be57d5a Fix Javadoc nits. be57d5a is described below commit be57d5ac62318fc9db1372a206b1b0c802d48616 Author: Gary Gregory <gardgreg...@gmail.com> AuthorDate: Wed Mar 13 12:30:47 2019 -0400 Fix Javadoc nits. --- .../commons/pool2/impl/GenericObjectPool.java | 2411 ++++++++++---------- 1 file changed, 1214 insertions(+), 1197 deletions(-) diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java index 7aff5f4..9158d3b 100644 --- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java @@ -1,1197 +1,1214 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.commons.pool2.impl; - -import org.apache.commons.pool2.ObjectPool; -import org.apache.commons.pool2.PoolUtils; -import org.apache.commons.pool2.PooledObject; -import org.apache.commons.pool2.PooledObjectFactory; -import org.apache.commons.pool2.PooledObjectState; -import org.apache.commons.pool2.SwallowedExceptionListener; -import org.apache.commons.pool2.TrackedUse; -import org.apache.commons.pool2.UsageTracking; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * A configurable {@link ObjectPool} implementation. - * <p> - * When coupled with the appropriate {@link PooledObjectFactory}, - * <code>GenericObjectPool</code> provides robust pooling functionality for - * arbitrary objects.</p> - * <p> - * Optionally, one may configure the pool to examine and possibly evict objects - * as they sit idle in the pool and to ensure that a minimum number of idle - * objects are available. This is performed by an "idle object eviction" thread, - * which runs asynchronously. Caution should be used when configuring this - * optional feature. Eviction runs contend with client threads for access to - * objects in the pool, so if they run too frequently performance issues may - * result.</p> - * <p> - * The pool can also be configured to detect and remove "abandoned" objects, - * i.e. objects that have been checked out of the pool but neither used nor - * returned before the configured - * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}. - * Abandoned object removal can be configured to happen when - * <code>borrowObject</code> is invoked and the pool is close to starvation, or - * it can be executed by the idle object evictor, or both. If pooled objects - * implement the {@link TrackedUse} interface, their last use will be queried - * using the <code>getLastUsed</code> method on that interface; otherwise - * abandonment is determined by how long an object has been checked out from - * the pool.</p> - * <p> - * Implementation note: To prevent possible deadlocks, care has been taken to - * ensure that no call to a factory method will occur within a synchronization - * block. See POOL-125 and DBCP-44 for more information.</p> - * <p> - * This class is intended to be thread-safe.</p> - * - * @see GenericKeyedObjectPool - * - * @param <T> Type of element pooled in this pool. - * - * @since 2.0 - */ -public class GenericObjectPool<T> extends BaseGenericObjectPool<T> - implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> { - - /** - * Creates a new <code>GenericObjectPool</code> using defaults from - * {@link GenericObjectPoolConfig}. - * - * @param factory The object factory to be used to create object instances - * used by this pool - */ - public GenericObjectPool(final PooledObjectFactory<T> factory) { - this(factory, new GenericObjectPoolConfig<T>()); - } - - /** - * Creates a new <code>GenericObjectPool</code> using a specific - * configuration. - * - * @param factory The object factory to be used to create object instances - * used by this pool - * @param config The configuration to use for this pool instance. The - * configuration is used by value. Subsequent changes to - * the configuration object will not be reflected in the - * pool. - */ - public GenericObjectPool(final PooledObjectFactory<T> factory, - final GenericObjectPoolConfig<T> config) { - - super(config, ONAME_BASE, config.getJmxNamePrefix()); - - if (factory == null) { - jmxUnregister(); // tidy up - throw new IllegalArgumentException("factory may not be null"); - } - this.factory = factory; - - idleObjects = new LinkedBlockingDeque<>(config.getFairness()); - - setConfig(config); - } - - /** - * Creates a new <code>GenericObjectPool</code> that tracks and destroys - * objects that are checked out, but never returned to the pool. - * - * @param factory The object factory to be used to create object instances - * used by this pool - * @param config The base pool configuration to use for this pool instance. - * The configuration is used by value. Subsequent changes to - * the configuration object will not be reflected in the - * pool. - * @param abandonedConfig Configuration for abandoned object identification - * and removal. The configuration is used by value. - */ - public GenericObjectPool(final PooledObjectFactory<T> factory, - final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) { - this(factory, config); - setAbandonedConfig(abandonedConfig); - } - - /** - * Returns the cap on the number of "idle" instances in the pool. If maxIdle - * is set too low on heavily loaded systems it is possible you will see - * objects being destroyed and almost immediately new objects being created. - * This is a result of the active threads momentarily returning objects - * faster than they are requesting them, causing the number of idle - * objects to rise above maxIdle. The best value for maxIdle for heavily - * loaded system will vary but the default is a good starting point. - * - * @return the maximum number of "idle" instances that can be held in the - * pool or a negative value if there is no limit - * - * @see #setMaxIdle - */ - @Override - public int getMaxIdle() { - return maxIdle; - } - - /** - * Returns the cap on the number of "idle" instances in the pool. If maxIdle - * is set too low on heavily loaded systems it is possible you will see - * objects being destroyed and almost immediately new objects being created. - * This is a result of the active threads momentarily returning objects - * faster than they are requesting them, causing the number of idle - * objects to rise above maxIdle. The best value for maxIdle for heavily - * loaded system will vary but the default is a good starting point. - * - * @param maxIdle - * The cap on the number of "idle" instances in the pool. Use a - * negative value to indicate an unlimited number of idle - * instances - * - * @see #getMaxIdle - */ - public void setMaxIdle(final int maxIdle) { - this.maxIdle = maxIdle; - } - - /** - * Sets the target for the minimum number of idle objects to maintain in - * the pool. This setting only has an effect if it is positive and - * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this - * is the case, an attempt is made to ensure that the pool has the required - * minimum number of instances during idle object eviction runs. - * <p> - * If the configured value of minIdle is greater than the configured value - * for maxIdle then the value of maxIdle will be used instead. - * - * @param minIdle - * The minimum number of objects. - * - * @see #getMinIdle() - * @see #getMaxIdle() - * @see #getTimeBetweenEvictionRunsMillis() - */ - public void setMinIdle(final int minIdle) { - this.minIdle = minIdle; - } - - /** - * Returns the target for the minimum number of idle objects to maintain in - * the pool. This setting only has an effect if it is positive and - * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this - * is the case, an attempt is made to ensure that the pool has the required - * minimum number of instances during idle object eviction runs. - * <p> - * If the configured value of minIdle is greater than the configured value - * for maxIdle then the value of maxIdle will be used instead. - * - * @return The minimum number of objects. - * - * @see #setMinIdle(int) - * @see #setMaxIdle(int) - * @see #setTimeBetweenEvictionRunsMillis(long) - */ - @Override - public int getMinIdle() { - final int maxIdleSave = getMaxIdle(); - if (this.minIdle > maxIdleSave) { - return maxIdleSave; - } - return minIdle; - } - - /** - * Gets whether or not abandoned object removal is configured for this pool. - * - * @return true if this pool is configured to detect and remove - * abandoned objects - */ - @Override - public boolean isAbandonedConfig() { - return abandonedConfig != null; - } - - /** - * Gets whether this pool identifies and logs any abandoned objects. - * - * @return {@code true} if abandoned object removal is configured for this - * pool and removal events are to be logged otherwise {@code false} - * - * @see AbandonedConfig#getLogAbandoned() - */ - @Override - public boolean getLogAbandoned() { - final AbandonedConfig ac = this.abandonedConfig; - return ac != null && ac.getLogAbandoned(); - } - - /** - * Gets whether a check is made for abandoned objects when an object is borrowed - * from this pool. - * - * @return {@code true} if abandoned object removal is configured to be - * activated by borrowObject otherwise {@code false} - * - * @see AbandonedConfig#getRemoveAbandonedOnBorrow() - */ - @Override - public boolean getRemoveAbandonedOnBorrow() { - final AbandonedConfig ac = this.abandonedConfig; - return ac != null && ac.getRemoveAbandonedOnBorrow(); - } - - /** - * Gets whether a check is made for abandoned objects when the evictor runs. - * - * @return {@code true} if abandoned object removal is configured to be - * activated when the evictor runs otherwise {@code false} - * - * @see AbandonedConfig#getRemoveAbandonedOnMaintenance() - */ - @Override - public boolean getRemoveAbandonedOnMaintenance() { - final AbandonedConfig ac = this.abandonedConfig; - return ac != null && ac.getRemoveAbandonedOnMaintenance(); - } - - /** - * Obtains the timeout before which an object will be considered to be - * abandoned by this pool. - * - * @return The abandoned object timeout in seconds if abandoned object - * removal is configured for this pool; Integer.MAX_VALUE otherwise. - * - * @see AbandonedConfig#getRemoveAbandonedTimeout() - */ - @Override - public int getRemoveAbandonedTimeout() { - final AbandonedConfig ac = this.abandonedConfig; - return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE; - } - - - /** - * Sets the base pool configuration. - * - * @param conf the new configuration to use. This is used by value. - * - * @see GenericObjectPoolConfig - */ - public void setConfig(final GenericObjectPoolConfig<T> conf) { - super.setConfig(conf); - setMaxIdle(conf.getMaxIdle()); - setMinIdle(conf.getMinIdle()); - setMaxTotal(conf.getMaxTotal()); - } - - /** - * Sets the abandoned object removal configuration. - * - * @param abandonedConfig the new configuration to use. This is used by value. - * - * @see AbandonedConfig - */ - public void setAbandonedConfig(final AbandonedConfig abandonedConfig) { - if (abandonedConfig == null) { - this.abandonedConfig = null; - } else { - this.abandonedConfig = new AbandonedConfig(); - this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned()); - this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter()); - this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow()); - this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance()); - this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeout()); - this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking()); - this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace()); - } - } - - /** - * Obtains a reference to the factory used to create, destroy and validate - * the objects used by this pool. - * - * @return the factory - */ - public PooledObjectFactory<T> getFactory() { - return factory; - } - - /** - * Equivalent to <code>{@link #borrowObject(long) - * borrowObject}({@link #getMaxWaitMillis()})</code>. - * <p> - * {@inheritDoc} - */ - @Override - public T borrowObject() throws Exception { - return borrowObject(getMaxWaitMillis()); - } - - /** - * Borrows an object from the pool using the specific waiting time which only - * applies if {@link #getBlockWhenExhausted()} is true. - * <p> - * If there is one or more idle instance available in the pool, then an - * idle instance will be selected based on the value of {@link #getLifo()}, - * activated and returned. If activation fails, or {@link #getTestOnBorrow() - * testOnBorrow} is set to <code>true</code> and validation fails, the - * instance is destroyed and the next available instance is examined. This - * continues until either a valid instance is returned or there are no more - * idle instances available. - * <p> - * If there are no idle instances available in the pool, behavior depends on - * the {@link #getMaxTotal() maxTotal}, (if applicable) - * {@link #getBlockWhenExhausted()} and the value passed in to the - * <code>borrowMaxWaitMillis</code> parameter. If the number of instances - * checked out from the pool is less than <code>maxTotal,</code> a new - * instance is created, activated and (if applicable) validated and returned - * to the caller. If validation fails, a <code>NoSuchElementException</code> - * is thrown. - * <p> - * If the pool is exhausted (no available idle instances and no capacity to - * create new ones), this method will either block (if - * {@link #getBlockWhenExhausted()} is true) or throw a - * <code>NoSuchElementException</code> (if - * {@link #getBlockWhenExhausted()} is false). The length of time that this - * method will block when {@link #getBlockWhenExhausted()} is true is - * determined by the value passed in to the <code>borrowMaxWaitMillis</code> - * parameter. - * <p> - * When the pool is exhausted, multiple calling threads may be - * simultaneously blocked waiting for instances to become available. A - * "fairness" algorithm has been implemented to ensure that threads receive - * available instances in request arrival order. - * - * @param borrowMaxWaitMillis The time to wait in milliseconds for an object - * to become available - * - * @return object instance from the pool - * - * @throws NoSuchElementException if an instance cannot be returned - * - * @throws Exception if an object instance cannot be returned due to an - * error - */ - public T borrowObject(final long borrowMaxWaitMillis) throws Exception { - assertOpen(); - - final AbandonedConfig ac = this.abandonedConfig; - if (ac != null && ac.getRemoveAbandonedOnBorrow() && - (getNumIdle() < 2) && - (getNumActive() > getMaxTotal() - 3) ) { - removeAbandoned(ac); - } - - PooledObject<T> p = null; - - // Get local copy of current config so it is consistent for entire - // method execution - final boolean blockWhenExhausted = getBlockWhenExhausted(); - - boolean create; - final long waitTime = System.currentTimeMillis(); - - while (p == null) { - create = false; - p = idleObjects.pollFirst(); - if (p == null) { - p = create(); - if (p != null) { - create = true; - } - } - if (blockWhenExhausted) { - if (p == null) { - if (borrowMaxWaitMillis < 0) { - p = idleObjects.takeFirst(); - } else { - p = idleObjects.pollFirst(borrowMaxWaitMillis, - TimeUnit.MILLISECONDS); - } - } - if (p == null) { - throw new NoSuchElementException( - "Timeout waiting for idle object"); - } - } else { - if (p == null) { - throw new NoSuchElementException("Pool exhausted"); - } - } - if (!p.allocate()) { - p = null; - } - - if (p != null) { - try { - factory.activateObject(p); - } catch (final Exception e) { - try { - destroy(p); - } catch (final Exception e1) { - // Ignore - activation failure is more important - } - p = null; - if (create) { - final NoSuchElementException nsee = new NoSuchElementException( - "Unable to activate object"); - nsee.initCause(e); - throw nsee; - } - } - if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) { - boolean validate = false; - Throwable validationThrowable = null; - try { - validate = factory.validateObject(p); - } catch (final Throwable t) { - PoolUtils.checkRethrow(t); - validationThrowable = t; - } - if (!validate) { - try { - destroy(p); - destroyedByBorrowValidationCount.incrementAndGet(); - } catch (final Exception e) { - // Ignore - validation failure is more important - } - p = null; - if (create) { - final NoSuchElementException nsee = new NoSuchElementException( - "Unable to validate object"); - nsee.initCause(validationThrowable); - throw nsee; - } - } - } - } - } - - updateStatsBorrow(p, System.currentTimeMillis() - waitTime); - - return p.getObject(); - } - - /** - * {@inheritDoc} - * <p> - * If {@link #getMaxIdle() maxIdle} is set to a positive value and the - * number of idle instances has reached this value, the returning instance - * is destroyed. - * <p> - * If {@link #getTestOnReturn() testOnReturn} == true, the returning - * instance is validated before being returned to the idle instance pool. In - * this case, if validation fails, the instance is destroyed. - * <p> - * Exceptions encountered destroying objects for any reason are swallowed - * but notified via a {@link SwallowedExceptionListener}. - */ - @Override - public void returnObject(final T obj) { - final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj)); - - if (p == null) { - if (!isAbandonedConfig()) { - throw new IllegalStateException( - "Returned object not currently part of this pool"); - } - return; // Object was abandoned and removed - } - - markReturningState(p); - - final long activeTime = p.getActiveTimeMillis(); - - if (getTestOnReturn() && !factory.validateObject(p)) { - try { - destroy(p); - } catch (final Exception e) { - swallowException(e); - } - try { - ensureIdle(1, false); - } catch (final Exception e) { - swallowException(e); - } - updateStatsReturn(activeTime); - return; - } - - try { - factory.passivateObject(p); - } catch (final Exception e1) { - swallowException(e1); - try { - destroy(p); - } catch (final Exception e) { - swallowException(e); - } - try { - ensureIdle(1, false); - } catch (final Exception e) { - swallowException(e); - } - updateStatsReturn(activeTime); - return; - } - - if (!p.deallocate()) { - throw new IllegalStateException( - "Object has already been returned to this pool or is invalid"); - } - - final int maxIdleSave = getMaxIdle(); - if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { - try { - destroy(p); - } catch (final Exception e) { - swallowException(e); - } - } else { - if (getLifo()) { - idleObjects.addFirst(p); - } else { - idleObjects.addLast(p); - } - if (isClosed()) { - // Pool closed while object was being added to idle objects. - // Make sure the returned object is destroyed rather than left - // in the idle object pool (which would effectively be a leak) - clear(); - } - } - updateStatsReturn(activeTime); - } - - /** - * {@inheritDoc} - * <p> - * Activation of this method decrements the active count and attempts to - * destroy the instance. - * - * @throws Exception if an exception occurs destroying the - * object - * @throws IllegalStateException if obj does not belong to this pool - */ - @Override - public void invalidateObject(final T obj) throws Exception { - final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj)); - if (p == null) { - if (isAbandonedConfig()) { - return; - } - throw new IllegalStateException( - "Invalidated object not currently part of this pool"); - } - synchronized (p) { - if (p.getState() != PooledObjectState.INVALID) { - destroy(p); - } - } - ensureIdle(1, false); - } - - /** - * Clears any objects sitting idle in the pool by removing them from the - * idle instance pool and then invoking the configured - * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each - * idle instance. - * <p> - * Implementation notes: - * <ul> - * <li>This method does not destroy or effect in any way instances that are - * checked out of the pool when it is invoked.</li> - * <li>Invoking this method does not prevent objects being returned to the - * idle instance pool, even during its execution. Additional instances may - * be returned while removed items are being destroyed.</li> - * <li>Exceptions encountered destroying idle instances are swallowed - * but notified via a {@link SwallowedExceptionListener}.</li> - * </ul> - */ - @Override - public void clear() { - PooledObject<T> p = idleObjects.poll(); - - while (p != null) { - try { - destroy(p); - } catch (final Exception e) { - swallowException(e); - } - p = idleObjects.poll(); - } - } - - @Override - public int getNumActive() { - return allObjects.size() - idleObjects.size(); - } - - @Override - public int getNumIdle() { - return idleObjects.size(); - } - - /** - * Closes the pool. Once the pool is closed, {@link #borrowObject()} will - * fail with IllegalStateException, but {@link #returnObject(Object)} and - * {@link #invalidateObject(Object)} will continue to work, with returned - * objects destroyed on return. - * <p> - * Destroys idle instances in the pool by invoking {@link #clear()}. - */ - @Override - public void close() { - if (isClosed()) { - return; - } - - synchronized (closeLock) { - if (isClosed()) { - return; - } - - // Stop the evictor before the pool is closed since evict() calls - // assertOpen() - stopEvitor(); - - closed = true; - // This clear removes any idle objects - clear(); - - jmxUnregister(); - - // Release any threads that were waiting for an object - idleObjects.interuptTakeWaiters(); - } - } - - /** - * {@inheritDoc} - * <p> - * Successive activations of this method examine objects in sequence, - * cycling through objects in oldest-to-youngest order. - */ - @Override - public void evict() throws Exception { - assertOpen(); - - if (idleObjects.size() > 0) { - - PooledObject<T> underTest = null; - final EvictionPolicy<T> evictionPolicy = getEvictionPolicy(); - - synchronized (evictionLock) { - final EvictionConfig evictionConfig = new EvictionConfig( - getMinEvictableIdleTimeMillis(), - getSoftMinEvictableIdleTimeMillis(), - getMinIdle()); - - final boolean testWhileIdle = getTestWhileIdle(); - - for (int i = 0, m = getNumTests(); i < m; i++) { - if (evictionIterator == null || !evictionIterator.hasNext()) { - evictionIterator = new EvictionIterator(idleObjects); - } - if (!evictionIterator.hasNext()) { - // Pool exhausted, nothing to do here - return; - } - - try { - underTest = evictionIterator.next(); - } catch (final NoSuchElementException nsee) { - // Object was borrowed in another thread - // Don't count this as an eviction test so reduce i; - i--; - evictionIterator = null; - continue; - } - - if (!underTest.startEvictionTest()) { - // Object was borrowed in another thread - // Don't count this as an eviction test so reduce i; - i--; - continue; - } - - // User provided eviction policy could throw all sorts of - // crazy exceptions. Protect against such an exception - // killing the eviction thread. - boolean evict; - try { - evict = evictionPolicy.evict(evictionConfig, underTest, - idleObjects.size()); - } catch (final Throwable t) { - // Slightly convoluted as SwallowedExceptionListener - // uses Exception rather than Throwable - PoolUtils.checkRethrow(t); - swallowException(new Exception(t)); - // Don't evict on error conditions - evict = false; - } - - if (evict) { - destroy(underTest); - destroyedByEvictorCount.incrementAndGet(); - } else { - if (testWhileIdle) { - boolean active = false; - try { - factory.activateObject(underTest); - active = true; - } catch (final Exception e) { - destroy(underTest); - destroyedByEvictorCount.incrementAndGet(); - } - if (active) { - if (!factory.validateObject(underTest)) { - destroy(underTest); - destroyedByEvictorCount.incrementAndGet(); - } else { - try { - factory.passivateObject(underTest); - } catch (final Exception e) { - destroy(underTest); - destroyedByEvictorCount.incrementAndGet(); - } - } - } - } - if (!underTest.endEvictionTest(idleObjects)) { - // TODO - May need to add code here once additional - // states are used - } - } - } - } - } - final AbandonedConfig ac = this.abandonedConfig; - if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { - removeAbandoned(ac); - } - } - - /** - * Tries to ensure that {@link #getMinIdle()} idle instances are available - * in the pool. - * - * @throws Exception If the associated factory throws an exception - * @since 2.4 - */ - public void preparePool() throws Exception { - if (getMinIdle() < 1) { - return; - } - ensureMinIdle(); - } - - /** - * Attempts to create a new wrapped pooled object. - * <p> - * If there are {@link #getMaxTotal()} objects already in circulation - * or in process of being created, this method returns null. - * - * @return The new wrapped pooled object - * - * @throws Exception if the object factory's {@code makeObject} fails - */ - private PooledObject<T> create() throws Exception { - int localMaxTotal = getMaxTotal(); - // This simplifies the code later in this method - if (localMaxTotal < 0) { - localMaxTotal = Integer.MAX_VALUE; - } - - final long localStartTimeMillis = System.currentTimeMillis(); - final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0); - - // Flag that indicates if create should: - // - TRUE: call the factory to create an object - // - FALSE: return null - // - null: loop and re-test the condition that determines whether to - // call the factory - Boolean create = null; - while (create == null) { - synchronized (makeObjectCountLock) { - final long newCreateCount = createCount.incrementAndGet(); - if (newCreateCount > localMaxTotal) { - // The pool is currently at capacity or in the process of - // making enough new objects to take it to capacity. - createCount.decrementAndGet(); - if (makeObjectCount == 0) { - // There are no makeObject() calls in progress so the - // pool is at capacity. Do not attempt to create a new - // object. Return and wait for an object to be returned - create = Boolean.FALSE; - } else { - // There are makeObject() calls in progress that might - // bring the pool to capacity. Those calls might also - // fail so wait until they complete and then re-test if - // the pool is at capacity or not. - makeObjectCountLock.wait(localMaxWaitTimeMillis); - } - } else { - // The pool is not at capacity. Create a new object. - makeObjectCount++; - create = Boolean.TRUE; - } - } - - // Do not block more if maxWaitTimeMillis is set. - if (create == null && - (localMaxWaitTimeMillis > 0 && - System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) { - create = Boolean.FALSE; - } - } - - if (!create.booleanValue()) { - return null; - } - - final PooledObject<T> p; - try { - p = factory.makeObject(); - } catch (final Throwable e) { - createCount.decrementAndGet(); - throw e; - } finally { - synchronized (makeObjectCountLock) { - makeObjectCount--; - makeObjectCountLock.notifyAll(); - } - } - - final AbandonedConfig ac = this.abandonedConfig; - if (ac != null && ac.getLogAbandoned()) { - p.setLogAbandoned(true); - // TODO: in 3.0, this can use the method defined on PooledObject - if (p instanceof DefaultPooledObject<?>) { - ((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace()); - } - } - - createdCount.incrementAndGet(); - allObjects.put(new IdentityWrapper<>(p.getObject()), p); - return p; - } - - /** - * Destroys a wrapped pooled object. - * - * @param toDestroy The wrapped pooled object to destroy - * - * @throws Exception If the factory fails to destroy the pooled object - * cleanly - */ - private void destroy(final PooledObject<T> toDestroy) throws Exception { - toDestroy.invalidate(); - idleObjects.remove(toDestroy); - allObjects.remove(new IdentityWrapper<>(toDestroy.getObject())); - try { - factory.destroyObject(toDestroy); - } finally { - destroyedCount.incrementAndGet(); - createCount.decrementAndGet(); - } - - if (idleObjects.isEmpty() && idleObjects.hasTakeWaiters()) { - // POOL-356. - // In case there are already threads waiting on something in the pool - // (e.g. idleObjects.takeFirst(); then we need to provide them a fresh instance. - // Otherwise they will be stuck forever (or until timeout) - final PooledObject<T> freshPooled = create(); - idleObjects.put(freshPooled); - } - } - - @Override - void ensureMinIdle() throws Exception { - ensureIdle(getMinIdle(), true); - } - - /** - * Tries to ensure that {@code idleCount} idle instances exist in the pool. - * <p> - * Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount} - * or the total number of objects (idle, checked out, or being created) reaches - * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless - * there are threads waiting to check out instances from the pool. - * - * @param idleCount the number of idle instances desired - * @param always true means create instances even if the pool has no threads waiting - * @throws Exception if the factory's makeObject throws - */ - private void ensureIdle(final int idleCount, final boolean always) throws Exception { - if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) { - return; - } - - while (idleObjects.size() < idleCount) { - final PooledObject<T> p = create(); - if (p == null) { - // Can't create objects, no reason to think another call to - // create will work. Give up. - break; - } - if (getLifo()) { - idleObjects.addFirst(p); - } else { - idleObjects.addLast(p); - } - } - if (isClosed()) { - // Pool closed while object was being added to idle objects. - // Make sure the returned object is destroyed rather than left - // in the idle object pool (which would effectively be a leak) - clear(); - } - } - - /** - * Creates an object, and place it into the pool. addObject() is useful for - * "pre-loading" a pool with idle objects. - * <p> - * If there is no capacity available to add to the pool, this is a no-op - * (no exception, no impact to the pool). </p> - */ - @Override - public void addObject() throws Exception { - assertOpen(); - if (factory == null) { - throw new IllegalStateException( - "Cannot add objects without a factory."); - } - final PooledObject<T> p = create(); - addIdleObject(p); - } - - /** - * Adds the provided wrapped pooled object to the set of idle objects for - * this pool. The object must already be part of the pool. If {@code p} - * is null, this is a no-op (no exception, but no impact on the pool). - * - * @param p The object to make idle - * - * @throws Exception If the factory fails to passivate the object - */ - private void addIdleObject(final PooledObject<T> p) throws Exception { - if (p != null) { - factory.passivateObject(p); - if (getLifo()) { - idleObjects.addFirst(p); - } else { - idleObjects.addLast(p); - } - } - } - - /** - * Calculates the number of objects to test in a run of the idle object - * evictor. - * - * @return The number of objects to test for validity - */ - private int getNumTests() { - final int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); - if (numTestsPerEvictionRun >= 0) { - return Math.min(numTestsPerEvictionRun, idleObjects.size()); - } - return (int) (Math.ceil(idleObjects.size() / - Math.abs((double) numTestsPerEvictionRun))); - } - - /** - * Recovers abandoned objects which have been checked out but - * not used since longer than the removeAbandonedTimeout. - * - * @param ac The configuration to use to identify abandoned objects - */ - private void removeAbandoned(final AbandonedConfig ac) { - // Generate a list of abandoned objects to remove - final long now = System.currentTimeMillis(); - final long timeout = - now - (ac.getRemoveAbandonedTimeout() * 1000L); - final ArrayList<PooledObject<T>> remove = new ArrayList<>(); - final Iterator<PooledObject<T>> it = allObjects.values().iterator(); - while (it.hasNext()) { - final PooledObject<T> pooledObject = it.next(); - synchronized (pooledObject) { - if (pooledObject.getState() == PooledObjectState.ALLOCATED && - pooledObject.getLastUsedTime() <= timeout) { - pooledObject.markAbandoned(); - remove.add(pooledObject); - } - } - } - - // Now remove the abandoned objects - final Iterator<PooledObject<T>> itr = remove.iterator(); - while (itr.hasNext()) { - final PooledObject<T> pooledObject = itr.next(); - if (ac.getLogAbandoned()) { - pooledObject.printStackTrace(ac.getLogWriter()); - } - try { - invalidateObject(pooledObject.getObject()); - } catch (final Exception e) { - e.printStackTrace(); - } - } - } - - - //--- Usage tracking support ----------------------------------------------- - - @Override - public void use(final T pooledObject) { - final AbandonedConfig ac = this.abandonedConfig; - if (ac != null && ac.getUseUsageTracking()) { - final PooledObject<T> wrapper = allObjects.get(new IdentityWrapper<>(pooledObject)); - wrapper.use(); - } - } - - - //--- JMX support ---------------------------------------------------------- - - private volatile String factoryType = null; - - /** - * Returns an estimate of the number of threads currently blocked waiting for - * an object from the pool. This is intended for monitoring only, not for - * synchronization control. - * - * @return The estimate of the number of threads currently blocked waiting - * for an object from the pool - */ - @Override - public int getNumWaiters() { - if (getBlockWhenExhausted()) { - return idleObjects.getTakeQueueLength(); - } - return 0; - } - - /** - * Returns the type - including the specific type rather than the generic - - * of the factory. - * - * @return A string representation of the factory type - */ - @Override - public String getFactoryType() { - // Not thread safe. Accept that there may be multiple evaluations. - if (factoryType == null) { - final StringBuilder result = new StringBuilder(); - result.append(factory.getClass().getName()); - result.append('<'); - final Class<?> pooledObjectType = - PoolImplUtils.getFactoryType(factory.getClass()); - result.append(pooledObjectType.getName()); - result.append('>'); - factoryType = result.toString(); - } - return factoryType; - } - - /** - * Provides information on all the objects in the pool, both idle (waiting - * to be borrowed) and active (currently borrowed). - * <p> - * Note: This is named listAllObjects so it is presented as an operation via - * JMX. That means it won't be invoked unless the explicitly requested - * whereas all attributes will be automatically requested when viewing the - * attributes for an object in a tool like JConsole. - * - * @return Information grouped on all the objects in the pool - */ - @Override - public Set<DefaultPooledObjectInfo> listAllObjects() { - final Set<DefaultPooledObjectInfo> result = - new HashSet<>(allObjects.size()); - for (final PooledObject<T> p : allObjects.values()) { - result.add(new DefaultPooledObjectInfo(p)); - } - return result; - } - - // --- configuration attributes -------------------------------------------- - - private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE; - private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE; - private final PooledObjectFactory<T> factory; - - - // --- internal attributes ------------------------------------------------- - - /* - * All of the objects currently associated with this pool in any state. It - * excludes objects that have been destroyed. The size of - * {@link #allObjects} will always be less than or equal to {@link - * #_maxActive}. Map keys are pooled objects, values are the PooledObject - * wrappers used internally by the pool. - */ - private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects = - new ConcurrentHashMap<>(); - /* - * The combined count of the currently created objects and those in the - * process of being created. Under load, it may exceed {@link #_maxActive} - * if multiple threads try and create a new object at the same time but - * {@link #create()} will ensure that there are never more than - * {@link #_maxActive} objects created at any one time. - */ - private final AtomicLong createCount = new AtomicLong(0); - private long makeObjectCount = 0; - private final Object makeObjectCountLock = new Object(); - private final LinkedBlockingDeque<PooledObject<T>> idleObjects; - - // JMX specific attributes - private static final String ONAME_BASE = - "org.apache.commons.pool2:type=GenericObjectPool,name="; - - // Additional configuration properties for abandoned object tracking - private volatile AbandonedConfig abandonedConfig = null; - - @Override - protected void toStringAppendFields(final StringBuilder builder) { - super.toStringAppendFields(builder); - builder.append(", factoryType="); - builder.append(factoryType); - builder.append(", maxIdle="); - builder.append(maxIdle); - builder.append(", minIdle="); - builder.append(minIdle); - builder.append(", factory="); - builder.append(factory); - builder.append(", allObjects="); - builder.append(allObjects); - builder.append(", createCount="); - builder.append(createCount); - builder.append(", idleObjects="); - builder.append(idleObjects); - builder.append(", abandonedConfig="); - builder.append(abandonedConfig); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.pool2.impl; + +import org.apache.commons.pool2.ObjectPool; +import org.apache.commons.pool2.PoolUtils; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.PooledObjectFactory; +import org.apache.commons.pool2.PooledObjectState; +import org.apache.commons.pool2.SwallowedExceptionListener; +import org.apache.commons.pool2.TrackedUse; +import org.apache.commons.pool2.UsageTracking; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A configurable {@link ObjectPool} implementation. + * <p> + * When coupled with the appropriate {@link PooledObjectFactory}, + * <code>GenericObjectPool</code> provides robust pooling functionality for + * arbitrary objects.</p> + * <p> + * Optionally, one may configure the pool to examine and possibly evict objects + * as they sit idle in the pool and to ensure that a minimum number of idle + * objects are available. This is performed by an "idle object eviction" thread, + * which runs asynchronously. Caution should be used when configuring this + * optional feature. Eviction runs contend with client threads for access to + * objects in the pool, so if they run too frequently performance issues may + * result.</p> + * <p> + * The pool can also be configured to detect and remove "abandoned" objects, + * i.e. objects that have been checked out of the pool but neither used nor + * returned before the configured + * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}. + * Abandoned object removal can be configured to happen when + * <code>borrowObject</code> is invoked and the pool is close to starvation, or + * it can be executed by the idle object evictor, or both. If pooled objects + * implement the {@link TrackedUse} interface, their last use will be queried + * using the <code>getLastUsed</code> method on that interface; otherwise + * abandonment is determined by how long an object has been checked out from + * the pool.</p> + * <p> + * Implementation note: To prevent possible deadlocks, care has been taken to + * ensure that no call to a factory method will occur within a synchronization + * block. See POOL-125 and DBCP-44 for more information.</p> + * <p> + * This class is intended to be thread-safe.</p> + * + * @see GenericKeyedObjectPool + * + * @param <T> Type of element pooled in this pool. + * + * @since 2.0 + */ +public class GenericObjectPool<T> extends BaseGenericObjectPool<T> + implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> { + + /** + * Creates a new <code>GenericObjectPool</code> using defaults from + * {@link GenericObjectPoolConfig}. + * + * @param factory The object factory to be used to create object instances + * used by this pool + */ + public GenericObjectPool(final PooledObjectFactory<T> factory) { + this(factory, new GenericObjectPoolConfig<T>()); + } + + /** + * Creates a new <code>GenericObjectPool</code> using a specific + * configuration. + * + * @param factory The object factory to be used to create object instances + * used by this pool + * @param config The configuration to use for this pool instance. The + * configuration is used by value. Subsequent changes to + * the configuration object will not be reflected in the + * pool. + */ + public GenericObjectPool(final PooledObjectFactory<T> factory, + final GenericObjectPoolConfig<T> config) { + + super(config, ONAME_BASE, config.getJmxNamePrefix()); + + if (factory == null) { + jmxUnregister(); // tidy up + throw new IllegalArgumentException("factory may not be null"); + } + this.factory = factory; + + idleObjects = new LinkedBlockingDeque<>(config.getFairness()); + + setConfig(config); + } + + /** + * Creates a new <code>GenericObjectPool</code> that tracks and destroys + * objects that are checked out, but never returned to the pool. + * + * @param factory The object factory to be used to create object instances + * used by this pool + * @param config The base pool configuration to use for this pool instance. + * The configuration is used by value. Subsequent changes to + * the configuration object will not be reflected in the + * pool. + * @param abandonedConfig Configuration for abandoned object identification + * and removal. The configuration is used by value. + */ + public GenericObjectPool(final PooledObjectFactory<T> factory, + final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) { + this(factory, config); + setAbandonedConfig(abandonedConfig); + } + + /** + * Returns the cap on the number of "idle" instances in the pool. If maxIdle + * is set too low on heavily loaded systems it is possible you will see + * objects being destroyed and almost immediately new objects being created. + * This is a result of the active threads momentarily returning objects + * faster than they are requesting them, causing the number of idle + * objects to rise above maxIdle. The best value for maxIdle for heavily + * loaded system will vary but the default is a good starting point. + * + * @return the maximum number of "idle" instances that can be held in the + * pool or a negative value if there is no limit + * + * @see #setMaxIdle + */ + @Override + public int getMaxIdle() { + return maxIdle; + } + + /** + * Returns the cap on the number of "idle" instances in the pool. If maxIdle + * is set too low on heavily loaded systems it is possible you will see + * objects being destroyed and almost immediately new objects being created. + * This is a result of the active threads momentarily returning objects + * faster than they are requesting them, causing the number of idle + * objects to rise above maxIdle. The best value for maxIdle for heavily + * loaded system will vary but the default is a good starting point. + * + * @param maxIdle + * The cap on the number of "idle" instances in the pool. Use a + * negative value to indicate an unlimited number of idle + * instances + * + * @see #getMaxIdle + */ + public void setMaxIdle(final int maxIdle) { + this.maxIdle = maxIdle; + } + + /** + * Sets the target for the minimum number of idle objects to maintain in + * the pool. This setting only has an effect if it is positive and + * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this + * is the case, an attempt is made to ensure that the pool has the required + * minimum number of instances during idle object eviction runs. + * <p> + * If the configured value of minIdle is greater than the configured value + * for maxIdle then the value of maxIdle will be used instead. + * </p> + * + * @param minIdle + * The minimum number of objects. + * + * @see #getMinIdle() + * @see #getMaxIdle() + * @see #getTimeBetweenEvictionRunsMillis() + */ + public void setMinIdle(final int minIdle) { + this.minIdle = minIdle; + } + + /** + * Returns the target for the minimum number of idle objects to maintain in + * the pool. This setting only has an effect if it is positive and + * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this + * is the case, an attempt is made to ensure that the pool has the required + * minimum number of instances during idle object eviction runs. + * <p> + * If the configured value of minIdle is greater than the configured value + * for maxIdle then the value of maxIdle will be used instead. + * </p> + * + * @return The minimum number of objects. + * + * @see #setMinIdle(int) + * @see #setMaxIdle(int) + * @see #setTimeBetweenEvictionRunsMillis(long) + */ + @Override + public int getMinIdle() { + final int maxIdleSave = getMaxIdle(); + if (this.minIdle > maxIdleSave) { + return maxIdleSave; + } + return minIdle; + } + + /** + * Gets whether or not abandoned object removal is configured for this pool. + * + * @return true if this pool is configured to detect and remove + * abandoned objects + */ + @Override + public boolean isAbandonedConfig() { + return abandonedConfig != null; + } + + /** + * Gets whether this pool identifies and logs any abandoned objects. + * + * @return {@code true} if abandoned object removal is configured for this + * pool and removal events are to be logged otherwise {@code false} + * + * @see AbandonedConfig#getLogAbandoned() + */ + @Override + public boolean getLogAbandoned() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null && ac.getLogAbandoned(); + } + + /** + * Gets whether a check is made for abandoned objects when an object is borrowed + * from this pool. + * + * @return {@code true} if abandoned object removal is configured to be + * activated by borrowObject otherwise {@code false} + * + * @see AbandonedConfig#getRemoveAbandonedOnBorrow() + */ + @Override + public boolean getRemoveAbandonedOnBorrow() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null && ac.getRemoveAbandonedOnBorrow(); + } + + /** + * Gets whether a check is made for abandoned objects when the evictor runs. + * + * @return {@code true} if abandoned object removal is configured to be + * activated when the evictor runs otherwise {@code false} + * + * @see AbandonedConfig#getRemoveAbandonedOnMaintenance() + */ + @Override + public boolean getRemoveAbandonedOnMaintenance() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null && ac.getRemoveAbandonedOnMaintenance(); + } + + /** + * Obtains the timeout before which an object will be considered to be + * abandoned by this pool. + * + * @return The abandoned object timeout in seconds if abandoned object + * removal is configured for this pool; Integer.MAX_VALUE otherwise. + * + * @see AbandonedConfig#getRemoveAbandonedTimeout() + */ + @Override + public int getRemoveAbandonedTimeout() { + final AbandonedConfig ac = this.abandonedConfig; + return ac != null ? ac.getRemoveAbandonedTimeout() : Integer.MAX_VALUE; + } + + + /** + * Sets the base pool configuration. + * + * @param conf the new configuration to use. This is used by value. + * + * @see GenericObjectPoolConfig + */ + public void setConfig(final GenericObjectPoolConfig<T> conf) { + super.setConfig(conf); + setMaxIdle(conf.getMaxIdle()); + setMinIdle(conf.getMinIdle()); + setMaxTotal(conf.getMaxTotal()); + } + + /** + * Sets the abandoned object removal configuration. + * + * @param abandonedConfig the new configuration to use. This is used by value. + * + * @see AbandonedConfig + */ + public void setAbandonedConfig(final AbandonedConfig abandonedConfig) { + if (abandonedConfig == null) { + this.abandonedConfig = null; + } else { + this.abandonedConfig = new AbandonedConfig(); + this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned()); + this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter()); + this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow()); + this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance()); + this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeout()); + this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking()); + this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace()); + } + } + + /** + * Obtains a reference to the factory used to create, destroy and validate + * the objects used by this pool. + * + * @return the factory + */ + public PooledObjectFactory<T> getFactory() { + return factory; + } + + /** + * Equivalent to <code>{@link #borrowObject(long) + * borrowObject}({@link #getMaxWaitMillis()})</code>. + * <p> + * {@inheritDoc} + * </p> + */ + @Override + public T borrowObject() throws Exception { + return borrowObject(getMaxWaitMillis()); + } + + /** + * Borrows an object from the pool using the specific waiting time which only + * applies if {@link #getBlockWhenExhausted()} is true. + * <p> + * If there is one or more idle instance available in the pool, then an + * idle instance will be selected based on the value of {@link #getLifo()}, + * activated and returned. If activation fails, or {@link #getTestOnBorrow() + * testOnBorrow} is set to <code>true</code> and validation fails, the + * instance is destroyed and the next available instance is examined. This + * continues until either a valid instance is returned or there are no more + * idle instances available. + * </p> + * <p> + * If there are no idle instances available in the pool, behavior depends on + * the {@link #getMaxTotal() maxTotal}, (if applicable) + * {@link #getBlockWhenExhausted()} and the value passed in to the + * <code>borrowMaxWaitMillis</code> parameter. If the number of instances + * checked out from the pool is less than <code>maxTotal,</code> a new + * instance is created, activated and (if applicable) validated and returned + * to the caller. If validation fails, a <code>NoSuchElementException</code> + * is thrown. + * </p> + * <p> + * If the pool is exhausted (no available idle instances and no capacity to + * create new ones), this method will either block (if + * {@link #getBlockWhenExhausted()} is true) or throw a + * <code>NoSuchElementException</code> (if + * {@link #getBlockWhenExhausted()} is false). The length of time that this + * method will block when {@link #getBlockWhenExhausted()} is true is + * determined by the value passed in to the <code>borrowMaxWaitMillis</code> + * parameter. + * </p> + * <p> + * When the pool is exhausted, multiple calling threads may be + * simultaneously blocked waiting for instances to become available. A + * "fairness" algorithm has been implemented to ensure that threads receive + * available instances in request arrival order. + * </p> + * + * @param borrowMaxWaitMillis The time to wait in milliseconds for an object + * to become available + * + * @return object instance from the pool + * + * @throws NoSuchElementException if an instance cannot be returned + * + * @throws Exception if an object instance cannot be returned due to an + * error + */ + public T borrowObject(final long borrowMaxWaitMillis) throws Exception { + assertOpen(); + + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getRemoveAbandonedOnBorrow() && + (getNumIdle() < 2) && + (getNumActive() > getMaxTotal() - 3) ) { + removeAbandoned(ac); + } + + PooledObject<T> p = null; + + // Get local copy of current config so it is consistent for entire + // method execution + final boolean blockWhenExhausted = getBlockWhenExhausted(); + + boolean create; + final long waitTime = System.currentTimeMillis(); + + while (p == null) { + create = false; + p = idleObjects.pollFirst(); + if (p == null) { + p = create(); + if (p != null) { + create = true; + } + } + if (blockWhenExhausted) { + if (p == null) { + if (borrowMaxWaitMillis < 0) { + p = idleObjects.takeFirst(); + } else { + p = idleObjects.pollFirst(borrowMaxWaitMillis, + TimeUnit.MILLISECONDS); + } + } + if (p == null) { + throw new NoSuchElementException( + "Timeout waiting for idle object"); + } + } else { + if (p == null) { + throw new NoSuchElementException("Pool exhausted"); + } + } + if (!p.allocate()) { + p = null; + } + + if (p != null) { + try { + factory.activateObject(p); + } catch (final Exception e) { + try { + destroy(p); + } catch (final Exception e1) { + // Ignore - activation failure is more important + } + p = null; + if (create) { + final NoSuchElementException nsee = new NoSuchElementException( + "Unable to activate object"); + nsee.initCause(e); + throw nsee; + } + } + if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) { + boolean validate = false; + Throwable validationThrowable = null; + try { + validate = factory.validateObject(p); + } catch (final Throwable t) { + PoolUtils.checkRethrow(t); + validationThrowable = t; + } + if (!validate) { + try { + destroy(p); + destroyedByBorrowValidationCount.incrementAndGet(); + } catch (final Exception e) { + // Ignore - validation failure is more important + } + p = null; + if (create) { + final NoSuchElementException nsee = new NoSuchElementException( + "Unable to validate object"); + nsee.initCause(validationThrowable); + throw nsee; + } + } + } + } + } + + updateStatsBorrow(p, System.currentTimeMillis() - waitTime); + + return p.getObject(); + } + + /** + * {@inheritDoc} + * <p> + * If {@link #getMaxIdle() maxIdle} is set to a positive value and the + * number of idle instances has reached this value, the returning instance + * is destroyed. + * </p> + * <p> + * If {@link #getTestOnReturn() testOnReturn} == true, the returning + * instance is validated before being returned to the idle instance pool. In + * this case, if validation fails, the instance is destroyed. + * </p> + * <p> + * Exceptions encountered destroying objects for any reason are swallowed + * but notified via a {@link SwallowedExceptionListener}. + * </p> + */ + @Override + public void returnObject(final T obj) { + final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj)); + + if (p == null) { + if (!isAbandonedConfig()) { + throw new IllegalStateException( + "Returned object not currently part of this pool"); + } + return; // Object was abandoned and removed + } + + markReturningState(p); + + final long activeTime = p.getActiveTimeMillis(); + + if (getTestOnReturn() && !factory.validateObject(p)) { + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } + updateStatsReturn(activeTime); + return; + } + + try { + factory.passivateObject(p); + } catch (final Exception e1) { + swallowException(e1); + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } + updateStatsReturn(activeTime); + return; + } + + if (!p.deallocate()) { + throw new IllegalStateException( + "Object has already been returned to this pool or is invalid"); + } + + final int maxIdleSave = getMaxIdle(); + if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + } else { + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(); + } + } + updateStatsReturn(activeTime); + } + + /** + * {@inheritDoc} + * <p> + * Activation of this method decrements the active count and attempts to + * destroy the instance. + * </p> + * + * @throws Exception if an exception occurs destroying the + * object + * @throws IllegalStateException if obj does not belong to this pool + */ + @Override + public void invalidateObject(final T obj) throws Exception { + final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj)); + if (p == null) { + if (isAbandonedConfig()) { + return; + } + throw new IllegalStateException( + "Invalidated object not currently part of this pool"); + } + synchronized (p) { + if (p.getState() != PooledObjectState.INVALID) { + destroy(p); + } + } + ensureIdle(1, false); + } + + /** + * Clears any objects sitting idle in the pool by removing them from the + * idle instance pool and then invoking the configured + * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each + * idle instance. + * <p> + * Implementation notes: + * </p> + * <ul> + * <li>This method does not destroy or effect in any way instances that are + * checked out of the pool when it is invoked.</li> + * <li>Invoking this method does not prevent objects being returned to the + * idle instance pool, even during its execution. Additional instances may + * be returned while removed items are being destroyed.</li> + * <li>Exceptions encountered destroying idle instances are swallowed + * but notified via a {@link SwallowedExceptionListener}.</li> + * </ul> + */ + @Override + public void clear() { + PooledObject<T> p = idleObjects.poll(); + + while (p != null) { + try { + destroy(p); + } catch (final Exception e) { + swallowException(e); + } + p = idleObjects.poll(); + } + } + + @Override + public int getNumActive() { + return allObjects.size() - idleObjects.size(); + } + + @Override + public int getNumIdle() { + return idleObjects.size(); + } + + /** + * Closes the pool. Once the pool is closed, {@link #borrowObject()} will + * fail with IllegalStateException, but {@link #returnObject(Object)} and + * {@link #invalidateObject(Object)} will continue to work, with returned + * objects destroyed on return. + * <p> + * Destroys idle instances in the pool by invoking {@link #clear()}. + * </p> + */ + @Override + public void close() { + if (isClosed()) { + return; + } + + synchronized (closeLock) { + if (isClosed()) { + return; + } + + // Stop the evictor before the pool is closed since evict() calls + // assertOpen() + stopEvitor(); + + closed = true; + // This clear removes any idle objects + clear(); + + jmxUnregister(); + + // Release any threads that were waiting for an object + idleObjects.interuptTakeWaiters(); + } + } + + /** + * {@inheritDoc} + * <p> + * Successive activations of this method examine objects in sequence, + * cycling through objects in oldest-to-youngest order. + * </p> + */ + @Override + public void evict() throws Exception { + assertOpen(); + + if (idleObjects.size() > 0) { + + PooledObject<T> underTest = null; + final EvictionPolicy<T> evictionPolicy = getEvictionPolicy(); + + synchronized (evictionLock) { + final EvictionConfig evictionConfig = new EvictionConfig( + getMinEvictableIdleTimeMillis(), + getSoftMinEvictableIdleTimeMillis(), + getMinIdle()); + + final boolean testWhileIdle = getTestWhileIdle(); + + for (int i = 0, m = getNumTests(); i < m; i++) { + if (evictionIterator == null || !evictionIterator.hasNext()) { + evictionIterator = new EvictionIterator(idleObjects); + } + if (!evictionIterator.hasNext()) { + // Pool exhausted, nothing to do here + return; + } + + try { + underTest = evictionIterator.next(); + } catch (final NoSuchElementException nsee) { + // Object was borrowed in another thread + // Don't count this as an eviction test so reduce i; + i--; + evictionIterator = null; + continue; + } + + if (!underTest.startEvictionTest()) { + // Object was borrowed in another thread + // Don't count this as an eviction test so reduce i; + i--; + continue; + } + + // User provided eviction policy could throw all sorts of + // crazy exceptions. Protect against such an exception + // killing the eviction thread. + boolean evict; + try { + evict = evictionPolicy.evict(evictionConfig, underTest, + idleObjects.size()); + } catch (final Throwable t) { + // Slightly convoluted as SwallowedExceptionListener + // uses Exception rather than Throwable + PoolUtils.checkRethrow(t); + swallowException(new Exception(t)); + // Don't evict on error conditions + evict = false; + } + + if (evict) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } else { + if (testWhileIdle) { + boolean active = false; + try { + factory.activateObject(underTest); + active = true; + } catch (final Exception e) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } + if (active) { + if (!factory.validateObject(underTest)) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } else { + try { + factory.passivateObject(underTest); + } catch (final Exception e) { + destroy(underTest); + destroyedByEvictorCount.incrementAndGet(); + } + } + } + } + if (!underTest.endEvictionTest(idleObjects)) { + // TODO - May need to add code here once additional + // states are used + } + } + } + } + } + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { + removeAbandoned(ac); + } + } + + /** + * Tries to ensure that {@link #getMinIdle()} idle instances are available + * in the pool. + * + * @throws Exception If the associated factory throws an exception + * @since 2.4 + */ + public void preparePool() throws Exception { + if (getMinIdle() < 1) { + return; + } + ensureMinIdle(); + } + + /** + * Attempts to create a new wrapped pooled object. + * <p> + * If there are {@link #getMaxTotal()} objects already in circulation + * or in process of being created, this method returns null. + * </p> + * + * @return The new wrapped pooled object + * + * @throws Exception if the object factory's {@code makeObject} fails + */ + private PooledObject<T> create() throws Exception { + int localMaxTotal = getMaxTotal(); + // This simplifies the code later in this method + if (localMaxTotal < 0) { + localMaxTotal = Integer.MAX_VALUE; + } + + final long localStartTimeMillis = System.currentTimeMillis(); + final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0); + + // Flag that indicates if create should: + // - TRUE: call the factory to create an object + // - FALSE: return null + // - null: loop and re-test the condition that determines whether to + // call the factory + Boolean create = null; + while (create == null) { + synchronized (makeObjectCountLock) { + final long newCreateCount = createCount.incrementAndGet(); + if (newCreateCount > localMaxTotal) { + // The pool is currently at capacity or in the process of + // making enough new objects to take it to capacity. + createCount.decrementAndGet(); + if (makeObjectCount == 0) { + // There are no makeObject() calls in progress so the + // pool is at capacity. Do not attempt to create a new + // object. Return and wait for an object to be returned + create = Boolean.FALSE; + } else { + // There are makeObject() calls in progress that might + // bring the pool to capacity. Those calls might also + // fail so wait until they complete and then re-test if + // the pool is at capacity or not. + makeObjectCountLock.wait(localMaxWaitTimeMillis); + } + } else { + // The pool is not at capacity. Create a new object. + makeObjectCount++; + create = Boolean.TRUE; + } + } + + // Do not block more if maxWaitTimeMillis is set. + if (create == null && + (localMaxWaitTimeMillis > 0 && + System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) { + create = Boolean.FALSE; + } + } + + if (!create.booleanValue()) { + return null; + } + + final PooledObject<T> p; + try { + p = factory.makeObject(); + } catch (final Throwable e) { + createCount.decrementAndGet(); + throw e; + } finally { + synchronized (makeObjectCountLock) { + makeObjectCount--; + makeObjectCountLock.notifyAll(); + } + } + + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getLogAbandoned()) { + p.setLogAbandoned(true); + // TODO: in 3.0, this can use the method defined on PooledObject + if (p instanceof DefaultPooledObject<?>) { + ((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace()); + } + } + + createdCount.incrementAndGet(); + allObjects.put(new IdentityWrapper<>(p.getObject()), p); + return p; + } + + /** + * Destroys a wrapped pooled object. + * + * @param toDestroy The wrapped pooled object to destroy + * + * @throws Exception If the factory fails to destroy the pooled object + * cleanly + */ + private void destroy(final PooledObject<T> toDestroy) throws Exception { + toDestroy.invalidate(); + idleObjects.remove(toDestroy); + allObjects.remove(new IdentityWrapper<>(toDestroy.getObject())); + try { + factory.destroyObject(toDestroy); + } finally { + destroyedCount.incrementAndGet(); + createCount.decrementAndGet(); + } + + if (idleObjects.isEmpty() && idleObjects.hasTakeWaiters()) { + // POOL-356. + // In case there are already threads waiting on something in the pool + // (e.g. idleObjects.takeFirst(); then we need to provide them a fresh instance. + // Otherwise they will be stuck forever (or until timeout) + final PooledObject<T> freshPooled = create(); + idleObjects.put(freshPooled); + } + } + + @Override + void ensureMinIdle() throws Exception { + ensureIdle(getMinIdle(), true); + } + + /** + * Tries to ensure that {@code idleCount} idle instances exist in the pool. + * <p> + * Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount} + * or the total number of objects (idle, checked out, or being created) reaches + * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless + * there are threads waiting to check out instances from the pool. + * </p> + * + * @param idleCount the number of idle instances desired + * @param always true means create instances even if the pool has no threads waiting + * @throws Exception if the factory's makeObject throws + */ + private void ensureIdle(final int idleCount, final boolean always) throws Exception { + if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) { + return; + } + + while (idleObjects.size() < idleCount) { + final PooledObject<T> p = create(); + if (p == null) { + // Can't create objects, no reason to think another call to + // create will work. Give up. + break; + } + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(); + } + } + + /** + * Creates an object, and place it into the pool. addObject() is useful for + * "pre-loading" a pool with idle objects. + * <p> + * If there is no capacity available to add to the pool, this is a no-op + * (no exception, no impact to the pool). </p> + */ + @Override + public void addObject() throws Exception { + assertOpen(); + if (factory == null) { + throw new IllegalStateException( + "Cannot add objects without a factory."); + } + final PooledObject<T> p = create(); + addIdleObject(p); + } + + /** + * Adds the provided wrapped pooled object to the set of idle objects for + * this pool. The object must already be part of the pool. If {@code p} + * is null, this is a no-op (no exception, but no impact on the pool). + * + * @param p The object to make idle + * + * @throws Exception If the factory fails to passivate the object + */ + private void addIdleObject(final PooledObject<T> p) throws Exception { + if (p != null) { + factory.passivateObject(p); + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + } + } + + /** + * Calculates the number of objects to test in a run of the idle object + * evictor. + * + * @return The number of objects to test for validity + */ + private int getNumTests() { + final int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); + if (numTestsPerEvictionRun >= 0) { + return Math.min(numTestsPerEvictionRun, idleObjects.size()); + } + return (int) (Math.ceil(idleObjects.size() / + Math.abs((double) numTestsPerEvictionRun))); + } + + /** + * Recovers abandoned objects which have been checked out but + * not used since longer than the removeAbandonedTimeout. + * + * @param ac The configuration to use to identify abandoned objects + */ + private void removeAbandoned(final AbandonedConfig ac) { + // Generate a list of abandoned objects to remove + final long now = System.currentTimeMillis(); + final long timeout = + now - (ac.getRemoveAbandonedTimeout() * 1000L); + final ArrayList<PooledObject<T>> remove = new ArrayList<>(); + final Iterator<PooledObject<T>> it = allObjects.values().iterator(); + while (it.hasNext()) { + final PooledObject<T> pooledObject = it.next(); + synchronized (pooledObject) { + if (pooledObject.getState() == PooledObjectState.ALLOCATED && + pooledObject.getLastUsedTime() <= timeout) { + pooledObject.markAbandoned(); + remove.add(pooledObject); + } + } + } + + // Now remove the abandoned objects + final Iterator<PooledObject<T>> itr = remove.iterator(); + while (itr.hasNext()) { + final PooledObject<T> pooledObject = itr.next(); + if (ac.getLogAbandoned()) { + pooledObject.printStackTrace(ac.getLogWriter()); + } + try { + invalidateObject(pooledObject.getObject()); + } catch (final Exception e) { + e.printStackTrace(); + } + } + } + + + //--- Usage tracking support ----------------------------------------------- + + @Override + public void use(final T pooledObject) { + final AbandonedConfig ac = this.abandonedConfig; + if (ac != null && ac.getUseUsageTracking()) { + final PooledObject<T> wrapper = allObjects.get(new IdentityWrapper<>(pooledObject)); + wrapper.use(); + } + } + + + //--- JMX support ---------------------------------------------------------- + + private volatile String factoryType = null; + + /** + * Returns an estimate of the number of threads currently blocked waiting for + * an object from the pool. This is intended for monitoring only, not for + * synchronization control. + * + * @return The estimate of the number of threads currently blocked waiting + * for an object from the pool + */ + @Override + public int getNumWaiters() { + if (getBlockWhenExhausted()) { + return idleObjects.getTakeQueueLength(); + } + return 0; + } + + /** + * Returns the type - including the specific type rather than the generic - + * of the factory. + * + * @return A string representation of the factory type + */ + @Override + public String getFactoryType() { + // Not thread safe. Accept that there may be multiple evaluations. + if (factoryType == null) { + final StringBuilder result = new StringBuilder(); + result.append(factory.getClass().getName()); + result.append('<'); + final Class<?> pooledObjectType = + PoolImplUtils.getFactoryType(factory.getClass()); + result.append(pooledObjectType.getName()); + result.append('>'); + factoryType = result.toString(); + } + return factoryType; + } + + /** + * Provides information on all the objects in the pool, both idle (waiting + * to be borrowed) and active (currently borrowed). + * <p> + * Note: This is named listAllObjects so it is presented as an operation via + * JMX. That means it won't be invoked unless the explicitly requested + * whereas all attributes will be automatically requested when viewing the + * attributes for an object in a tool like JConsole. + * </p> + * + * @return Information grouped on all the objects in the pool + */ + @Override + public Set<DefaultPooledObjectInfo> listAllObjects() { + final Set<DefaultPooledObjectInfo> result = + new HashSet<>(allObjects.size()); + for (final PooledObject<T> p : allObjects.values()) { + result.add(new DefaultPooledObjectInfo(p)); + } + return result; + } + + // --- configuration attributes -------------------------------------------- + + private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE; + private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE; + private final PooledObjectFactory<T> factory; + + + // --- internal attributes ------------------------------------------------- + + /* + * All of the objects currently associated with this pool in any state. It + * excludes objects that have been destroyed. The size of + * {@link #allObjects} will always be less than or equal to {@link + * #_maxActive}. Map keys are pooled objects, values are the PooledObject + * wrappers used internally by the pool. + */ + private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects = + new ConcurrentHashMap<>(); + /* + * The combined count of the currently created objects and those in the + * process of being created. Under load, it may exceed {@link #_maxActive} + * if multiple threads try and create a new object at the same time but + * {@link #create()} will ensure that there are never more than + * {@link #_maxActive} objects created at any one time. + */ + private final AtomicLong createCount = new AtomicLong(0); + private long makeObjectCount = 0; + private final Object makeObjectCountLock = new Object(); + private final LinkedBlockingDeque<PooledObject<T>> idleObjects; + + // JMX specific attributes + private static final String ONAME_BASE = + "org.apache.commons.pool2:type=GenericObjectPool,name="; + + // Additional configuration properties for abandoned object tracking + private volatile AbandonedConfig abandonedConfig = null; + + @Override + protected void toStringAppendFields(final StringBuilder builder) { + super.toStringAppendFields(builder); + builder.append(", factoryType="); + builder.append(factoryType); + builder.append(", maxIdle="); + builder.append(maxIdle); + builder.append(", minIdle="); + builder.append(minIdle); + builder.append(", factory="); + builder.append(factory); + builder.append(", allObjects="); + builder.append(allObjects); + builder.append(", createCount="); + builder.append(createCount); + builder.append(", idleObjects="); + builder.append(idleObjects); + builder.append(", abandonedConfig="); + builder.append(abandonedConfig); + } + +}