/**
   * 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<PooledObject<T>> remove = new ArrayList<>();
    Iterator<PooledObject<T>> it = allObjects.values().iterator();
    while (it.hasNext()) {
      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
    Iterator<PooledObject<T>> itr = remove.iterator();
    while (itr.hasNext()) {
      PooledObject<T> pooledObject = itr.next();
      if (ac.getLogAbandoned()) {
        pooledObject.printStackTrace(ac.getLogWriter());
      }
      try {
        invalidateObject(pooledObject.getObject());
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
  /**
   * 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();
    long newCreateCount = createCount.incrementAndGet();
    if (localMaxTotal > -1 && newCreateCount > localMaxTotal
        || newCreateCount > Integer.MAX_VALUE) {
      createCount.decrementAndGet();
      return null;
    }

    final PooledObject<T> 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;
  }
 @Override
 public void use(T pooledObject) {
   AbandonedConfig ac = this.abandonedConfig;
   if (ac != null && ac.getUseUsageTracking()) {
     PooledObject<T> wrapper = allObjects.get(new IdentityWrapper<>(pooledObject));
     wrapper.use();
   }
 }
 /**
  * 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) {
   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());
   }
 }
  /**
   * {@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();
  }
 /**
  * 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;
 }
 /**
  * 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();
 }
 /**
  * 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();
 }