/**
  * 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());
   }
 }
  /**
   * 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();
  }
 /**
  * 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();
 }