/**
   * Test if a connection can be obtained
   *
   * @param subject Optional subject
   * @param cri Optional CRI
   * @return True if possible; otherwise false
   */
  protected boolean internalTestConnection(ConnectionRequestInfo cri, Subject subject) {
    boolean result = false;
    ConnectionListener cl = null;
    try {
      boolean separateNoTx = false;

      if (noTxSeparatePools) {
        separateNoTx = clf.isTransactional();
      }

      Object key = getKey(subject, cri, separateNoTx);
      ManagedConnectionPool mcp = getManagedConnectionPool(key, subject, cri);

      if (mcp.getStatistics().getAvailableCount() > 0) {
        cl = mcp.getConnection(subject, cri);
        result = true;
      }
    } catch (Throwable ignored) {
      // Ignore
    } finally {
      if (cl != null) {
        try {
          returnConnection(cl, false);
        } catch (ResourceException ire) {
          // Ignore
        }
      }
    }

    return result;
  }
  /**
   * Gets simple connection listener that wraps connection.
   *
   * @param subject Subject instance
   * @param cri Connection request info
   * @param mcp The managed connection pool
   * @return connection listener
   * @throws ResourceException ResourceException
   */
  private ConnectionListener getSimpleConnection(
      final Subject subject, final ConnectionRequestInfo cri, final ManagedConnectionPool mcp)
      throws ResourceException {
    ConnectionListener cl = null;

    try {
      // Get connection from the managed connection pool
      cl = mcp.getConnection(subject, cri);

      if (trace) log.tracef("Got connection from pool: %s", cl);

      return cl;
    } catch (ResourceException re) {
      if (re instanceof RetryableException) {
        if (log.isDebugEnabled())
          log.debug("Got a RetryableException - trying to reinitialize the pool");

        // Make sure that the managed connection pool is running
        if (!mcp.isRunning()) mcp.reenable();

        // Getting connection from pool
        cl = mcp.getConnection(subject, cri);

        if (trace) log.tracef("Got connection from pool (retried): %s", cl);

        return cl;
      } else {
        throw re;
      }
    }
  }
  /** {@inheritDoc} */
  public ConnectionListener findConnectionListener(ManagedConnection mc, Object connection) {
    for (ManagedConnectionPool mcp : mcpPools.values()) {
      ConnectionListener cl = mcp.findConnectionListener(mc, connection);
      if (cl != null) return cl;
    }

    return null;
  }
  /** {@inheritDoc} */
  public void returnConnection(ConnectionListener cl, boolean kill) throws ResourceException {
    cl.setTrackByTx(false);
    // Get connection listener pool
    ManagedConnectionPool mcp = cl.getManagedConnectionPool();

    // Return connection to the pool
    mcp.returnConnection(cl, kill);

    if (trace) log.tracef("Returning connection to pool %s", cl);
  }
  /** {@inheritDoc} */
  public void shutdown() {
    log.debug(poolName + ": shutdown");

    Iterator<ManagedConnectionPool> it = mcpPools.values().iterator();
    while (it.hasNext()) {
      ManagedConnectionPool mcp = it.next();
      mcp.shutdown();
    }

    mcpPools.clear();
  }
  /**
   * Gets new connection listener if necessary instance with transaction. This method is package
   * protected beacause it is intended only for test case use. Please don't use it in your
   * production code.
   *
   * @param trackByTransaction transaction instance
   * @param mcp pool instance
   * @param subject subject instance
   * @param cri connection request info
   * @return connection listener instance
   * @throws ResourceException ResourceException
   */
  ConnectionListener getTransactionNewConnection(
      Transaction trackByTransaction,
      ManagedConnectionPool mcp,
      Subject subject,
      ConnectionRequestInfo cri)
      throws ResourceException {
    // Need a new one for this transaction
    // This must be done outside the tx local lock, otherwise
    // the tx timeout won't work and get connection can do a lot of other work
    // with many opportunities for deadlocks.
    // Instead we do a double check after we got the transaction to see
    // whether another thread beat us to the punch.
    ConnectionListener cl = mcp.getConnection(subject, cri);
    if (trace)
      log.tracef(
          "Got connection from pool tracked by transaction=%s tx=%s", cl, trackByTransaction);

    TransactionSynchronizationRegistry tsr = getTransactionSynchronizationRegistry();
    Lock lock = getLock();
    try {
      lock.lockInterruptibly();
    } catch (InterruptedException ie) {
      Thread.interrupted();

      throw new ResourceException(bundle.unableObtainLock(), ie);
    }
    try {
      // Check we weren't racing with another transaction
      ConnectionListener other = (ConnectionListener) tsr.getResource(mcp);

      if (other != null) {
        mcp.returnConnection(cl, false);

        if (trace)
          log.tracef(
              "Another thread already got a connection tracked by transaction=%s tx=%s",
              other, trackByTransaction);

        cl = other;
      }

      // This is the connection for this transaction
      cl.setTrackByTx(true);
      tsr.putResource(mcp, cl);

      if (trace)
        log.tracef(
            "Using connection from pool tracked by transaction=%s tx=%s", cl, trackByTransaction);

      return cl;
    } finally {
      lock.unlock();
    }
  }
  /** {@inheritDoc} */
  public synchronized void emptyManagedConnectionPool(ManagedConnectionPool pool) {
    log.debug(poolName + ": emptyManagedConnectionPool(" + pool + ")");

    if (pool != null) {
      // We only consider removal if there are more than 1 managed connection pool
      if (mcpPools.size() > 1) {
        Iterator<ManagedConnectionPool> it = mcpPools.values().iterator();

        while (it.hasNext()) {
          ManagedConnectionPool other = it.next();
          if (other == pool && pool.isEmpty()) {
            pool.shutdown();
            it.remove();
            break;
          }
        }
      }
    }
  }
  /** {@inheritDoc} */
  public synchronized void flush(FlushMode mode) {
    log.debug(poolName + ": flush(" + mode + ")");

    Set<ManagedConnectionPool> clearMcpPools = new HashSet<ManagedConnectionPool>();

    Iterator<ManagedConnectionPool> it = mcpPools.values().iterator();
    while (it.hasNext()) {
      ManagedConnectionPool mcp = it.next();
      mcp.flush(mode);

      if (mcp.isEmpty()) clearMcpPools.add(mcp);
    }

    if (clearMcpPools.size() > 0) {
      for (ManagedConnectionPool mcp : clearMcpPools) {
        mcp.shutdown();
        mcpPools.values().remove(mcp);
      }
    }
  }