/** * {@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; EvictionPolicy<T> 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); } }
/** * Borrow 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(long borrowMaxWaitMillis) throws Exception { assertOpen(); 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 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(); }