All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.pool2.impl.GenericObjectPool Maven / Gradle / Ivy

There is a newer version: 2.12.0
Show newest version
/*
 * 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 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;

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;

/**
 * A configurable {@link ObjectPool} implementation.
 * 

* When coupled with the appropriate {@link PooledObjectFactory}, * GenericObjectPool provides robust pooling functionality for * arbitrary objects.

*

* 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.

*

* 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 * borrowObject 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 getLastUsed method on that interface; otherwise * abandonment is determined by how long an object has been checked out from * the pool.

*

* 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.

*

* This class is intended to be thread-safe.

* * @see GenericKeyedObjectPool * * @param Type of element pooled in this pool. * * @version $Revision: 1679560 $ * * @since 2.0 */ public class GenericObjectPool extends BaseGenericObjectPool implements ObjectPool, GenericObjectPoolMXBean, UsageTracking { /** * Create a new GenericObjectPool using defaults from * {@link GenericObjectPoolConfig}. * * @param factory The object factory to be used to create object instances * used by this pool */ public GenericObjectPool(PooledObjectFactory factory) { this(factory, new GenericObjectPoolConfig()); } /** * Create a new GenericObjectPool 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(PooledObjectFactory factory, GenericObjectPoolConfig 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); startEvictor(getTimeBetweenEvictionRunsMillis()); } /** * Create a new GenericObjectPool 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(PooledObjectFactory factory, GenericObjectPoolConfig config, 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 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 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(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. *

* 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(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. *

* 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() { int maxIdleSave = getMaxIdle(); if (this.minIdle > maxIdleSave) { return maxIdleSave; } else { return minIdle; } } /** * 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; } /** * Will this pool identify and log 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() { AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getLogAbandoned(); } /** * Will a check be 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() { AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getRemoveAbandonedOnBorrow(); } /** * Will a check be 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() { AbandonedConfig ac = this.abandonedConfig; return ac != null && ac.getRemoveAbandonedOnMaintenance(); } /** * Obtain 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() { 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(GenericObjectPoolConfig conf) { setLifo(conf.getLifo()); setMaxIdle(conf.getMaxIdle()); setMinIdle(conf.getMinIdle()); setMaxTotal(conf.getMaxTotal()); setMaxWaitMillis(conf.getMaxWaitMillis()); setBlockWhenExhausted(conf.getBlockWhenExhausted()); setTestOnCreate(conf.getTestOnCreate()); setTestOnBorrow(conf.getTestOnBorrow()); setTestOnReturn(conf.getTestOnReturn()); setTestWhileIdle(conf.getTestWhileIdle()); setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun()); setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis()); setTimeBetweenEvictionRunsMillis( conf.getTimeBetweenEvictionRunsMillis()); setSoftMinEvictableIdleTimeMillis( conf.getSoftMinEvictableIdleTimeMillis()); setEvictionPolicyClassName(conf.getEvictionPolicyClassName()); } /** * Sets the abandoned object removal configuration. * * @param abandonedConfig the new configuration to use. This is used by value. * * @see AbandonedConfig */ public void setAbandonedConfig(AbandonedConfig abandonedConfig) throws IllegalArgumentException { 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()); } } /** * Obtain a reference to the factory used to create, destroy and validate * the objects used by this pool. * * @return the factory */ public PooledObjectFactory getFactory() { return factory; } /** * Equivalent to {@link #borrowObject(long) * borrowObject}({@link #getMaxWaitMillis()}). *

* {@inheritDoc} */ @Override public T borrowObject() throws Exception { return borrowObject(getMaxWaitMillis()); } /** * Borrow an object from the pool using the specific waiting time which only * applies if {@link #getBlockWhenExhausted()} is true. *

* 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 true 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. *

* 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 * borrowMaxWaitMillis parameter. If the number of instances * checked out from the pool is less than maxTotal, a new * instance is created, activated and (if applicable) validated and returned * to the caller. If validation fails, a NoSuchElementException * is thrown. *

* 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 * NoSuchElementException (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 borrowMaxWaitMillis * parameter. *

* 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(long borrowMaxWaitMillis) throws Exception { assertOpen(); AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) && (getNumActive() > getMaxTotal() - 3) ) { removeAbandoned(ac); } PooledObject p = null; // Get local copy of current config so it is consistent for entire // method execution boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create; long waitTime = System.currentTimeMillis(); while (p == null) { create = false; if (blockWhenExhausted) { p = idleObjects.pollFirst(); if (p == null) { p = create(); if (p != null) { create = true; } } 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"); } if (!p.allocate()) { p = null; } } else { p = idleObjects.pollFirst(); if (p == null) { p = create(); if (p != null) { create = true; } } if (p == null) { throw new NoSuchElementException("Pool exhausted"); } if (!p.allocate()) { p = null; } } if (p != null) { try { factory.activateObject(p); } catch (Exception e) { try { destroy(p); } catch (Exception e1) { // Ignore - activation failure is more important } p = null; if (create) { 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 (Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { try { destroy(p); destroyedByBorrowValidationCount.incrementAndGet(); } catch (Exception e) { // Ignore - validation failure is more important } p = null; if (create) { NoSuchElementException nsee = new NoSuchElementException( "Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; } } } } } updateStatsBorrow(p, System.currentTimeMillis() - waitTime); return p.getObject(); } /** * {@inheritDoc} *

* 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. *

* 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. *

* Exceptions encountered destroying objects for any reason are swallowed * but notified via a {@link SwallowedExceptionListener}. */ @Override public void returnObject(T obj) { PooledObject p = allObjects.get(new IdentityWrapper(obj)); if (p == null) { if (!isAbandonedConfig()) { throw new IllegalStateException( "Returned object not currently part of this pool"); } else { return; // Object was abandoned and removed } } synchronized(p) { final PooledObjectState state = p.getState(); if (state != PooledObjectState.ALLOCATED) { throw new IllegalStateException( "Object has already been returned to this pool or is invalid"); } else { p.markReturning(); // Keep from being marked abandoned } } long activeTime = p.getActiveTimeMillis(); if (getTestOnReturn()) { if (!factory.validateObject(p)) { try { destroy(p); } catch (Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } } try { factory.passivateObject(p); } catch (Exception e1) { swallowException(e1); try { destroy(p); } catch (Exception e) { swallowException(e); } try { ensureIdle(1, false); } catch (Exception e) { swallowException(e); } updateStatsReturn(activeTime); return; } if (!p.deallocate()) { throw new IllegalStateException( "Object has already been returned to this pool or is invalid"); } int maxIdleSave = getMaxIdle(); if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { try { destroy(p); } catch (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} *

* 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(T obj) throws Exception { PooledObject p = allObjects.get(new IdentityWrapper(obj)); if (p == null) { if (isAbandonedConfig()) { return; } else { 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. *

* Implementation notes: *

    *
  • This method does not destroy or effect in any way instances that are * checked out of the pool when it is invoked.
  • *
  • 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.
  • *
  • Exceptions encountered destroying idle instances are swallowed * but notified via a {@link SwallowedExceptionListener}.
  • *
*/ @Override public void clear() { PooledObject p = idleObjects.poll(); while (p != null) { try { destroy(p); } catch (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. *

* 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() startEvictor(-1L); closed = true; // This clear removes any idle objects clear(); jmxUnregister(); // Release any threads that were waiting for an object idleObjects.interuptTakeWaiters(); } } /** * {@inheritDoc} *

* 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 underTest = null; EvictionPolicy evictionPolicy = getEvictionPolicy(); synchronized (evictionLock) { EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleTimeMillis(), getSoftMinEvictableIdleTimeMillis(), getMinIdle()); 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 (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 (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 (Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(underTest)) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(underTest); } catch (Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } } } } if (!underTest.endEvictionTest(idleObjects)) { // TODO - May need to add code here once additional // states are used } } } } } 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. *

* 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 create() throws Exception { int localMaxTotal = getMaxTotal(); long newCreateCount = createCount.incrementAndGet(); if (localMaxTotal > -1 && newCreateCount > localMaxTotal || newCreateCount > Integer.MAX_VALUE) { createCount.decrementAndGet(); return null; } final PooledObject p; try { p = factory.makeObject(); } catch (Exception e) { createCount.decrementAndGet(); throw e; } AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getLogAbandoned()) { p.setLogAbandoned(true); } createdCount.incrementAndGet(); allObjects.put(new IdentityWrapper(p.getObject()), p); return p; } /** * Destroys a wrapped pooled object. * * @param toDestory The wrapped pooled object to destroy * * @throws Exception If the factory fails to destroy the pooled object * cleanly */ private void destroy(PooledObject toDestory) throws Exception { toDestory.invalidate(); idleObjects.remove(toDestory); allObjects.remove(new IdentityWrapper(toDestory.getObject())); try { factory.destroyObject(toDestory); } finally { destroyedCount.incrementAndGet(); createCount.decrementAndGet(); } } @Override void ensureMinIdle() throws Exception { ensureIdle(getMinIdle(), true); } /** * Tries to ensure that {@code idleCount} idle instances exist in the pool. *

* 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(int idleCount, boolean always) throws Exception { if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) { return; } while (idleObjects.size() < idleCount) { PooledObject 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(); } } /** * Create an object, and place it into the pool. addObject() is useful for * "pre-loading" a pool with idle objects. *

* If there is no capacity available to add to the pool, this is a no-op * (no exception, no impact to the pool).

*/ @Override public void addObject() throws Exception { assertOpen(); if (factory == null) { throw new IllegalStateException( "Cannot add objects without a factory."); } PooledObject p = create(); addIdleObject(p); } /** * Add 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(PooledObject p) throws Exception { if (p != null) { factory.passivateObject(p); if (getLifo()) { idleObjects.addFirst(p); } else { idleObjects.addLast(p); } } } /** * Calculate 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() { int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); if (numTestsPerEvictionRun >= 0) { return Math.min(numTestsPerEvictionRun, idleObjects.size()); } else { return (int) (Math.ceil(idleObjects.size() / Math.abs((double) numTestsPerEvictionRun))); } } /** * Recover 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(AbandonedConfig ac) { // Generate a list of abandoned objects to remove final long now = System.currentTimeMillis(); final long timeout = now - (ac.getRemoveAbandonedTimeout() * 1000L); ArrayList> remove = new ArrayList>(); Iterator> it = allObjects.values().iterator(); while (it.hasNext()) { PooledObject pooledObject = it.next(); synchronized (pooledObject) { if (pooledObject.getState() == PooledObjectState.ALLOCATED && pooledObject.getLastUsedTime() <= timeout) { pooledObject.markAbandoned(); remove.add(pooledObject); } } } // Now remove the abandoned objects Iterator> itr = remove.iterator(); while (itr.hasNext()) { PooledObject pooledObject = itr.next(); if (ac.getLogAbandoned()) { pooledObject.printStackTrace(ac.getLogWriter()); } try { invalidateObject(pooledObject.getObject()); } catch (Exception e) { e.printStackTrace(); } } } //--- Usage tracking support ----------------------------------------------- @Override public void use(T pooledObject) { AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getUseUsageTracking()) { PooledObject wrapper = allObjects.get(new IdentityWrapper(pooledObject)); wrapper.use(); } } //--- JMX support ---------------------------------------------------------- private volatile String factoryType = null; /** * Return 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(); } else { return 0; } } /** * Return 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) { StringBuilder result = new StringBuilder(); result.append(factory.getClass().getName()); result.append('<'); 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). *

* 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 listAllObjects() { Set result = new HashSet(allObjects.size()); for (PooledObject 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 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, PooledObject> allObjects = new ConcurrentHashMap, PooledObject>(); /* * 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 final LinkedBlockingDeque> 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; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy