/**
   * Obtains a connection.
   *
   * @param route where the connection should point to
   * @return a connection that can be used to communicate along the given route
   */
  public ManagedClientConnection getConnection(final HttpRoute route, final Object state) {
    Args.notNull(route, "Route");
    assertStillUp();

    if (log.isDebugEnabled()) {
      log.debug("Get connection for route " + route);
    }

    synchronized (this) {
      Asserts.check(managedConn == null, MISUSE_MESSAGE);

      // check re-usability of the connection
      boolean recreate = false;
      boolean shutdown = false;

      // Kill the connection if it expired.
      closeExpiredConnections();

      if (uniquePoolEntry.connection.isOpen()) {
        final RouteTracker tracker = uniquePoolEntry.tracker;
        shutdown =
            (tracker == null
                || // can happen if method is aborted
                !tracker.toRoute().equals(route));
      } else {
        // If the connection is not open, create a new PoolEntry,
        // as the connection may have been marked not reusable,
        // due to aborts -- and the PoolEntry should not be reused
        // either.  There's no harm in recreating an entry if
        // the connection is closed.
        recreate = true;
      }

      if (shutdown) {
        recreate = true;
        try {
          uniquePoolEntry.shutdown();
        } catch (final IOException iox) {
          log.debug("Problem shutting down connection.", iox);
        }
      }

      if (recreate) {
        uniquePoolEntry = new PoolEntry();
      }

      managedConn = new ConnAdapter(uniquePoolEntry, route);

      return managedConn;
    }
  }
  public void releaseConnection(
      final ManagedClientConnection conn, final long validDuration, final TimeUnit timeUnit) {
    Args.check(
        conn instanceof ConnAdapter,
        "Connection class mismatch, " + "connection not obtained from this manager");
    assertStillUp();

    if (log.isDebugEnabled()) {
      log.debug("Releasing connection " + conn);
    }

    final ConnAdapter sca = (ConnAdapter) conn;
    synchronized (sca) {
      if (sca.poolEntry == null) {
        return; // already released
      }
      final ClientConnectionManager manager = sca.getManager();
      Asserts.check(manager == this, "Connection not obtained from this manager");
      try {
        // make sure that the response has been read completely
        if (sca.isOpen() && (this.alwaysShutDown || !sca.isMarkedReusable())) {
          if (log.isDebugEnabled()) {
            log.debug("Released connection open but not reusable.");
          }

          // make sure this connection will not be re-used
          // we might have gotten here because of a shutdown trigger
          // shutdown of the adapter also clears the tracked route
          sca.shutdown();
        }
      } catch (final IOException iox) {
        if (log.isDebugEnabled()) {
          log.debug("Exception shutting down released connection.", iox);
        }
      } finally {
        sca.detach();
        synchronized (this) {
          managedConn = null;
          lastReleaseTime = System.currentTimeMillis();
          if (validDuration > 0) {
            connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
          } else {
            connectionExpiresTime = Long.MAX_VALUE;
          }
        }
      }
    }
  }
  public void closeIdleConnections(final long idletime, final TimeUnit tunit) {
    assertStillUp();

    // idletime can be 0 or negative, no problem there
    Args.notNull(tunit, "Time unit");

    synchronized (this) {
      if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
        final long cutoff = System.currentTimeMillis() - tunit.toMillis(idletime);
        if (lastReleaseTime <= cutoff) {
          try {
            uniquePoolEntry.close();
          } catch (final IOException iox) {
            // ignore
            log.debug("Problem closing idle connection.", iox);
          }
        }
      }
    }
  }