/** Creates a new connection. */
  private UserPoolItem createConnection(
      ManagedConnectionFactory mcf,
      Subject subject,
      ConnectionRequestInfo info,
      UserPoolItem oldPoolItem)
      throws ResourceException {
    boolean isValid = false;
    ManagedPoolItem poolItem = null;

    try {
      ManagedConnection mConn = mcf.createManagedConnection(subject, info);

      if (mConn == null)
        throw new ResourceException(
            L.l("'{0}' did not return a connection from createManagedConnection", mcf));

      poolItem = new ManagedPoolItem(this, mcf, mConn);

      UserPoolItem userPoolItem;

      // Ensure the connection is still valid
      userPoolItem = poolItem.toActive(subject, info, oldPoolItem);

      if (userPoolItem == null) {
        throw new ResourceException(L.l("Connection '{0}' was not valid on creation", poolItem));
      }

      _connectionCreateCountTotal.incrementAndGet();

      synchronized (_connectionPool) {
        _connectionPool.add(poolItem);
      }

      poolItem = null;
      isValid = true;

      return userPoolItem;
    } finally {
      if (!isValid) {
        _connectionFailCountTotal.incrementAndGet();
        _lastFailTime = CurrentTime.getCurrentTime();
      }

      // server/308b - connection removed on rollback-only, when it's
      // theoretically possible to reuse it
      if (poolItem != null) poolItem.destroy();
    }
  }
  /** Starts creation of an overflow connection. */
  private boolean startCreateOverflow() throws ResourceException {
    int size = _connectionPool.size();
    int createCount = _createCount.incrementAndGet();

    if (createCount + size <= _maxConnections + _maxOverflowConnections) return true;

    _createCount.decrementAndGet();
    String message =
        L.l(
            "{0} cannot create overflow connection after {1}ms"
                + " (pool-size={2}"
                + ", max-connections={3}"
                + ", create-count={4}"
                + ", max-create-connections={5}"
                + ", max-overflow-connections={6})",
            this,
            _connectionWaitTimeout,
            _connectionPool.size(),
            _maxConnections,
            _createCount.get(),
            _maxCreateConnections,
            _maxOverflowConnections);

    HealthStatusService.updateCurrentHealthStatus(this, HealthStatus.WARNING, message);

    throw new ResourceException(message);
  }
  /** Sets the max number of connections simultaneously creating */
  public void setMaxCreateConnections(int maxConnections) throws ConfigException {
    if (maxConnections == 0)
      throw new ConfigException(L.l("max-create-connections '0' must be at least 1."));

    _maxCreateConnections = maxConnections;

    if (maxConnections < 0) _maxCreateConnections = Integer.MAX_VALUE / 2;
  }
  /** Initialize the connection manager. */
  public Object init(ManagedConnectionFactory mcf) throws ConfigException, ResourceException {
    if (!_lifecycle.toInit()) return null;

    _mcf = mcf;

    if (_name == null) {
      int v = _idGen.incrementAndGet();

      _name = mcf.getClass().getSimpleName() + "-" + v;
    }

    if (_tm == null)
      throw new ConfigException(L.l("the connection manager needs a transaction manager."));

    _idlePool = new IdlePoolSet(_maxIdleCount);

    _connectionTime = MeterService.createActiveTimeMeter("Resin|Database|Connection");
    _idleTime = MeterService.createActiveTimeMeter("Resin|Database|Idle");
    _queryTime = MeterService.createActiveTimeMeter("Resin|Database|Query");

    registerSelf();

    _alarm = new WeakAlarm(this);

    if (!(mcf instanceof ValidatingManagedConnectionFactory)) {
      // never check
      _lastValidCheckTime = Long.MAX_VALUE / 2;
    }

    // recover any resources on startup
    if (_isEnableXA) {
      Subject subject = null;
      ManagedConnection mConn = mcf.createManagedConnection(subject, null);

      try {
        XAResource xa = mConn.getXAResource();

        _tm.recover(xa);
      } catch (NotSupportedException e) {
        log.finer(e.toString());
      } catch (Throwable e) {
        log.log(Level.FINER, e.toString(), e);
      } finally {
        mConn.destroy();
      }
    }

    return mcf.createConnectionFactory(this);
  }
  /** Allocates a connection from the idle pool. */
  private UserPoolItem allocateIdleConnection(
      ManagedConnectionFactory mcf,
      Subject subject,
      ConnectionRequestInfo info,
      UserPoolItem oldPoolItem)
      throws ResourceException {
    while (_lifecycle.isActive()) {
      ManagedConnection mConn;

      long now = CurrentTime.getCurrentTime();

      if (_lastValidCheckTime + 15000L < now) {
        _lastValidCheckTime = now;

        if (mcf instanceof ValidatingManagedConnectionFactory) {
          ValidatingManagedConnectionFactory vmcf;
          vmcf = (ValidatingManagedConnectionFactory) mcf;

          validate(vmcf);
        }
      }

      ManagedPoolItem poolItem = null;

      while (true) {
        // asks the Driver's ManagedConnectionFactory to match an
        // idle connection
        synchronized (_connectionPool) {
          mConn = mcf.matchManagedConnections(_idlePool, subject, info);

          // If there are no more idle connections, return null
          if (mConn == null) return null;

          // remove can fail for threading reasons, so only succeed if it works.
          if (!_idlePool.remove(mConn)) {
            mConn = null;
          }
        }

        if (mConn != null) {
          poolItem = findPoolItem(mConn);

          if (poolItem == null)
            throw new IllegalStateException(
                L.l("Unexpected non-matching PoolItem found for {0}", mConn));

          break;
        }
      }

      try {
        // Ensure the connection is still valid
        UserPoolItem userPoolItem;
        userPoolItem = poolItem.toActive(subject, info, oldPoolItem);

        if (userPoolItem != null) {
          poolItem = null;
          return userPoolItem;
        }
      } finally {
        if (poolItem != null) poolItem.destroy();
      }
    }

    return null;
  }
  /**
   * Allocates the pool item for a connection, creating one if necessary.
   *
   * @param mcf the driver's ManagedConnectionFactory for creating pooled connections
   * @param subject the user's authentication credentials
   * @param info the user's extra connection information
   */
  UserPoolItem allocatePoolConnection(
      ManagedConnectionFactory mcf,
      Subject subject,
      ConnectionRequestInfo info,
      UserPoolItem oldPoolItem)
      throws ResourceException {
    long expireTime = CurrentTime.getCurrentTimeActual() + _connectionWaitTimeout;

    if (!_lifecycle.isActive()) {
      throw new IllegalStateException(
          L.l("{0}: Can't allocate connection because the connection pool is closed.", this));
    }

    do {
      UserPoolItem userPoolItem = allocateIdleConnection(mcf, subject, info, oldPoolItem);

      if (userPoolItem != null) return userPoolItem;

      // if no item in pool, try to create one
      if (startCreateConnection()) {
        try {
          return createConnection(mcf, subject, info, oldPoolItem);
        } finally {
          finishCreateConnection();
        }
      }
    } while (_lifecycle.isActive() && waitForAvailableConnection(expireTime));

    if (!_lifecycle.isActive())
      throw new IllegalStateException(
          L.l("{0}: Can't allocate connection because the connection pool is closed.", this));

    String message =
        (this
            + " pool throttled create timeout"
            + " (pool-size="
            + _connectionPool.size()
            + ", max-connections="
            + _maxConnections
            + ", create-count="
            + _createCount.get()
            + ", max-create-connections="
            + _maxCreateConnections
            + ")");

    HealthStatusService.updateCurrentHealthStatus(this, HealthStatus.WARNING, message);

    if (startCreateOverflow()) {
      try {
        return createConnection(mcf, subject, info, oldPoolItem);
      } finally {
        finishCreateConnection();
      }
    }

    message =
        (this
            + " pool overflow failed to create"
            + " (pool-size="
            + _connectionPool.size()
            + ", max-connections="
            + _maxConnections
            + ", create-count="
            + _createCount.get()
            + ", max-create-connections="
            + _maxCreateConnections
            + ")");

    HealthStatusService.updateCurrentHealthStatus(this, HealthStatus.CRITICAL, message);

    throw new ResourceException(
        L.l("Can't create overflow connection connection-max={0}", _maxConnections));
  }