/** Allocates a connection. */
  private Object allocateConnection(
      ManagedConnectionFactory mcf, Subject subject, ConnectionRequestInfo info)
      throws ResourceException {
    UserPoolItem userPoolItem = null;

    try {
      while (true) {
        userPoolItem = null;
        UserTransactionImpl transaction = _tm.getUserTransaction();

        if (transaction != null) userPoolItem = allocateShared(transaction, mcf, subject, info);

        if (userPoolItem == null) userPoolItem = allocatePoolConnection(mcf, subject, info, null);

        Object userConn;

        userConn = userPoolItem.allocateUserConnection();

        if (userConn != null) {
          userPoolItem = null;
          return userConn;
        }

        userPoolItem.close();
      }
    } finally {
      if (userPoolItem != null) userPoolItem.close();
    }
  }
  /** Try to share the connection. */
  boolean share(UserPoolItem userPoolItem) {
    if (this == userPoolItem.getOwnPoolItem()) return true;
    else if (_mConn == null) // already closed
    return false;
    else if (!_cm.isShareable()) // not shareable
    return false;
    else if (_mcf != userPoolItem.getManagedConnectionFactory()) return false;
    else if (_subject != userPoolItem.getSubject()) return false;
    else if (_requestInfo != userPoolItem.getInfo()) return false;
    else if (_hasConnectionError) // had a fatal error
    return false;

    // skip for now
    if (true) return false;

    userPoolItem.associate(this, _mcf, _subject, _requestInfo);

    return true;
  }
  /**
   * Make this connection active.
   *
   * @return true if the pool item is valid, false if it should be removed.
   */
  synchronized UserPoolItem toActive(
      Subject subject, ConnectionRequestInfo info, UserPoolItem oldPoolItem)
      throws ResourceException {
    long now = Alarm.getCurrentTime();

    long maxIdleTime = _cm.getMaxIdleTime();
    long maxPoolTime = _cm.getMaxPoolTime();

    if (_hasConnectionError) return null;
    else if (0 < maxIdleTime && _poolEventTime + maxIdleTime < now) return null;
    else if (0 < maxPoolTime && _poolStartTime + maxPoolTime < now) return null;
    else if (_shareHead != null)
      throw new IllegalStateException(L.l("trying to activate active pool item."));

    _poolEventTime = now;
    _isXATransaction = _xaResource != null; // disable LT-optim by default

    UserPoolItem userPoolItem = null;

    if (oldPoolItem != null) {
      Object uConn = oldPoolItem.getUserConnection();

      if (uConn != null) _mConn.associateConnection(uConn);

      oldPoolItem.associatePoolItem(this);

      userPoolItem = oldPoolItem;
    } else userPoolItem = new UserPoolItem(_cm, this);

    if (!isValid(subject, info, userPoolItem)) return null;

    _subject = subject;
    _requestInfo = info;
    userPoolItem.associate(this, _mcf, subject, info);

    if (log.isLoggable(Level.FINE)) log.fine("allocate " + this);

    if (_cm.getSaveAllocationStackTrace())
      _allocationStackTrace =
          new IllegalStateException(L.l("Connection {0} allocation stack trace", this));

    return userPoolItem;
  }
  private void fillIdlePool() {
    int count = _minIdleCount;

    try {
      while (_connectionPool.size() < _minIdleCount && count-- >= 0 && _lifecycle.isActive()) {
        Subject subject = null;
        ConnectionRequestInfo info = null;

        UserPoolItem userPoolItem;

        userPoolItem = createConnection(_mcf, subject, info, null);

        if (userPoolItem != null) userPoolItem.toIdle();
      }
    } catch (Exception e) {
      e.printStackTrace();

      log.log(Level.FINE, e.toString(), e);
    }
  }
  /** Returns true for a valid connection. */
  boolean isValid(Subject subject, ConnectionRequestInfo requestInfo, UserPoolItem userPoolItem) {
    try {
      ManagedConnection mConn = getManagedConnection();

      if (mConn == null) return false;

      Object userConn = userPoolItem.getUserConnection();

      if (userConn == null) {
        userConn = mConn.getConnection(subject, requestInfo);

        userPoolItem.setUserConnection(userConn);
      }

      return userConn != null;
    } catch (ResourceException e) {
      log.log(Level.WARNING, e.toString(), e);

      return false;
    }
  }
  /**
   * Use the item only if it's already been used for the current transaction and is available.
   * allocateXA returns the same connection for the following case:
   *
   * <pre>
   * UserTransaction.begin();
   *
   * conn = ds.getConnection();
   * ...
   * conn.close();
   *
   * conn = ds.getConnection();
   * ...
   * conn.close();
   * </pre>
   *
   * <p>Nested connections are not reused.
   *
   * @param xid the current transaction id
   * @return true if the pool item has been allocated
   */
  UserPoolItem allocateXA(
      ManagedConnectionFactory mcf, Subject subject, ConnectionRequestInfo info) {
    if (_mConn == null) // already closed
    return null;
    else if (_subject != subject) return null;
    else if (_requestInfo != info) return null;
    else if (_mcf != mcf) return null;
    else if (_shareHead != null && !_cm.isShareable()) // is currently in use
    return null;
    /* server/14g9, #2708
    else if (_hasConnectionError) // had a fatal error
      return null;
    */

    if (log.isLoggable(Level.FINER)) log.finer("sharing xa-pool item: " + this);

    UserPoolItem userPoolItem = new UserPoolItem(_cm);
    userPoolItem.associate(this, _mcf, _subject, _requestInfo);

    return userPoolItem;
  }
  /** Restores the delegation for the entire chain. */
  private void clearXid() {
    _xid = null;

    UserPoolItem shareHead = _shareHead;
    _shareHead = null;

    ManagedPoolItem xaPtr = _xaNext;
    _xaHead = null;
    _xaNext = null;

    UserPoolItem assignedUserItem = null;
    for (UserPoolItem ptr = shareHead; ptr != null; ptr = ptr.getShareNext()) {
      if (ptr.getOwnPoolItem() == this) {
        assignedUserItem = ptr;
        break;
      }
    }

    for (UserPoolItem ptr = shareHead; ptr != null; ptr = ptr.getShareNext()) {
      if (assignedUserItem == null && ptr.getOwnPoolItem() == null) {
        ptr.associatePoolItem(this);
        assignedUserItem = ptr;
      }

      try {
        ptr.reassociatePoolItem();
      } catch (Throwable e) {
        log.log(Level.WARNING, e.toString(), e);
      }
    }

    while (xaPtr != null) {
      ManagedPoolItem next = xaPtr._xaNext;
      xaPtr._xaNext = null;
      xaPtr._xaHead = null;

      xaPtr.clearXid();

      xaPtr = next;
    }

    if (assignedUserItem != null) {
      // _shareHead = assignedUserItem;
      // _shareHead.setShareNext(null);
    } else if (_hasConnectionError) {
      destroy();
    } else {
      toIdle();
    }
  }
  /**
   * Notifies that an application has closed the connection.
   *
   * <p>Remove and close the UserPoolItem associated with the PoolItem. If it's the last
   * UserPoolItem, move to the idle state.
   */
  public void connectionClosed(ConnectionEvent event) {
    Object handle = event.getConnectionHandle();

    if (!_hasConnectionError && handle == null && _shareHead != null) {
      log.fine(
          L.l(
              "JCA close event '{0}' for {1} did not have a connection handle.  Please notify the JCA resource provider.",
              event, _mConn));
    }

    UserPoolItem ptr = _shareHead;
    UserPoolItem prev = null;

    while (ptr != null) {
      UserPoolItem next = ptr.getShareNext();

      Object userConn = ptr.getUserConnection();

      if (userConn == handle || handle == null) {
        if (prev != null) prev.setShareNext(next);
        else _shareHead = next;

        ptr.close();
      } else prev = ptr;

      ptr = next;
    }

    if (_shareHead == null) {
      toIdle();
      return;
    }
  }
  /** Closes the connection. */
  void destroy() {
    ManagedConnection mConn = _mConn;
    _mConn = null;

    UserTransactionImpl transaction = _transaction;
    _transaction = null;

    if (mConn == null) return;

    _cm.removeItem(this, mConn);

    UserPoolItem userItem = _shareHead;

    if (log.isLoggable(Level.FINE)) log.fine("connection pool destroy " + this);

    try {
      while (userItem != null) {
        UserPoolItem next = userItem.getShareNext();

        userItem.close();

        userItem = next;
      }

      if (transaction != null) transaction.delistPoolItem(this, XAResource.TMFAIL);
    } catch (Throwable e) {
      log.log(Level.FINE, e.toString(), e);
    }

    try {
      mConn.destroy();
    } catch (Exception e) {
      log.log(Level.FINE, e.toString(), e);
    } finally {
      _cm.getConnectionTimeProbe().end(_connectionStartTime);
    }
  }