gt:                                 " [" + route + "][" + state + "]");
context: /*
   * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java $
   * $Revision: 677240 $
   * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $
   *
   * ====================================================================
   *
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.http.impl.conn.tsccm;
  
  import java.util.Date;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Queue;
  import java.util.LinkedList;
  import java.util.Map;
  import java.util.concurrent.locks.Condition;
  import java.util.concurrent.TimeUnit;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.apache.http.conn.routing.HttpRoute;
  import org.apache.http.conn.ClientConnectionOperator;
  import org.apache.http.conn.ConnectionPoolTimeoutException;
  import org.apache.http.conn.params.ConnPerRoute;
  import org.apache.http.conn.params.ConnManagerParams;
  import org.apache.http.params.HttpParams;
  
  
  /**
   * A connection pool that maintains connections by route.
   * This class is derived from <code>MultiThreadedHttpConnectionManager</code>
   * in HttpClient 3.x, see there for original authors. It implements the same
   * algorithm for connection re-use and connection-per-host enforcement:
   * <ul>
   * <li>connections are re-used only for the exact same route</li>
   * <li>connection limits are enforced per route rather than per host</li>
   * </ul>
   * Note that access to the pool datastructures is synchronized via the
   * {@link AbstractConnPool#poolLock poolLock} in the base class,
   * not via <code>synchronized</code> methods.
   *
   * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
   * @author and others
   */
  public class ConnPoolByRoute extends AbstractConnPool {
          
      private final Log log = LogFactory.getLog(getClass());
  
      /** Connection operator for this pool */
      protected final ClientConnectionOperator operator;
      
      /** The list of free connections */
      protected Queue<BasicPoolEntry> freeConnections;
  
      /** The list of WaitingThreads waiting for a connection */
      protected Queue<WaitingThread> waitingThreads;
  
      /**
       * A map of route-specific pools.
       * Keys are of class {@link HttpRoute},
       * values of class {@link RouteSpecificPool}.
       */
      protected final Map<HttpRoute, RouteSpecificPool> routeToPool;
  
      protected final int maxTotalConnections;
      
      private final ConnPerRoute connPerRoute;
      
      /**
       * Creates a new connection pool, managed by route.
       */
      public ConnPoolByRoute(final ClientConnectionOperator operator, final HttpParams params) {
          super();
          if (operator == null) {
              throw new IllegalArgumentException("Connection operator may not be null");
          }
          this.operator = operator;
          
          freeConnections = createFreeConnQueue();
          waitingThreads  = createWaitingThreadQueue();
          routeToPool     = createRouteToPoolMap();
          maxTotalConnections = ConnManagerParams
              .getMaxTotalConnections(params);
          connPerRoute = ConnManagerParams
              .getMaxConnectionsPerRoute(params);
      }
  
  
      /**
       * Creates the queue for {@link #freeConnections}.
       * Called once by the constructor.
       *
       * @return  a queue
       */
      protected Queue<BasicPoolEntry> createFreeConnQueue() {
          return new LinkedList<BasicPoolEntry>();
      }
  
      /**
       * Creates the queue for {@link #waitingThreads}.
       * Called once by the constructor.
       *
       * @return  a queue
       */
      protected Queue<WaitingThread> createWaitingThreadQueue() {
          return new LinkedList<WaitingThread>();
      }
  
      /**
       * Creates the map for {@link #routeToPool}.
       * Called once by the constructor.
       *
       * @return  a map
       */
      protected Map<HttpRoute, RouteSpecificPool> createRouteToPoolMap() {
          return new HashMap<HttpRoute, RouteSpecificPool>();
      }
  
  
      /**
       * Creates a new route-specific pool.
       * Called by {@link #getRoutePool} when necessary.
       *
       * @param route     the route
       *
       * @return  the new pool
       */
      protected RouteSpecificPool newRouteSpecificPool(HttpRoute route) {
          return new RouteSpecificPool(route, connPerRoute.getMaxForRoute(route));
      }
  
  
      /**
       * Creates a new waiting thread.
       * Called by {@link #getRoutePool} when necessary.
       *
       * @param cond      the condition to wait for
       * @param rospl     the route specific pool, or <code>null</code>
       *
       * @return  a waiting thread representation
       */
      protected WaitingThread newWaitingThread(Condition cond,
                                               RouteSpecificPool rospl) {
          return new WaitingThread(cond, rospl);
      }
  
  
      /**
       * Get a route-specific pool of available connections.
       *
       * @param route   the route
       * @param create    whether to create the pool if it doesn't exist
       *
       * @return  the pool for the argument route,
       *     never <code>null</code> if <code>create</code> is <code>true</code>
       */
      protected RouteSpecificPool getRoutePool(HttpRoute route,
                                               boolean create) {
          RouteSpecificPool rospl = null;
          poolLock.lock();
          try {
  
              rospl = routeToPool.get(route);
              if ((rospl == null) && create) {
                  // no pool for this route yet (or anymore)
                  rospl = newRouteSpecificPool(route);
                  routeToPool.put(route, rospl);
              }
  
          } finally {
              poolLock.unlock();
          }
  
          return rospl;
      }
  
  
      //@@@ consider alternatives for gathering statistics
      public int getConnectionsInPool(HttpRoute route) {
  
          poolLock.lock();
          try {
              // don't allow a pool to be created here!
              RouteSpecificPool rospl = getRoutePool(route, false);
              return (rospl != null) ? rospl.getEntryCount() : 0;
  
          } finally {
              poolLock.unlock();
          }
      }
      
      @Override
      public PoolEntryRequest requestPoolEntry(
              final HttpRoute route,
              final Object state) {
          
          final WaitingThreadAborter aborter = new WaitingThreadAborter();
          
          return new PoolEntryRequest() {
          
              public void abortRequest() {
                  poolLock.lock();
                  try {
                      aborter.abort();
                  } finally {
                      poolLock.unlock();
                  }
              }
              
              public BasicPoolEntry getPoolEntry(
                      long timeout,
                      TimeUnit tunit)
                          throws InterruptedException, ConnectionPoolTimeoutException {
                  return getEntryBlocking(route, state, timeout, tunit, aborter);
              }
              
          };
      }
  
      /**
       * Obtains a pool entry with a connection within the given timeout.
       * If a {@link WaitingThread} is used to block, {@link WaitingThreadAborter#setWaitingThread(WaitingThread)}
       * must be called before blocking, to allow the thread to be interrupted.
       *
       * @param route     the route for which to get the connection
       * @param timeout   the timeout, 0 or negative for no timeout
       * @param tunit     the unit for the <code>timeout</code>,
       *                  may be <code>null</code> only if there is no timeout
       * @param aborter   an object which can abort a {@link WaitingThread}.
       *
       * @return  pool entry holding a connection for the route
       *
       * @throws ConnectionPoolTimeoutException
       *         if the timeout expired
       * @throws InterruptedException
       *         if the calling thread was interrupted
       */
      protected BasicPoolEntry getEntryBlocking(
                                     HttpRoute route, Object state,
                                     long timeout, TimeUnit tunit,
                                     WaitingThreadAborter aborter)
          throws ConnectionPoolTimeoutException, InterruptedException {
  
          Date deadline = null;
          if (timeout > 0) {
              deadline = new Date
                  (System.currentTimeMillis() + tunit.toMillis(timeout));
          }
  
          BasicPoolEntry entry = null;
          poolLock.lock();
          try {
  
              RouteSpecificPool rospl = getRoutePool(route, true);
              WaitingThread waitingThread = null;
  
              while (entry == null) {
  
                  if (isShutDown) {
                      throw new IllegalStateException
                          ("Connection pool shut down.");
                  }
  
                  if (log.isDebugEnabled()) {
                      log.debug("Total connections kept alive: " + freeConnections.size()); 
                      log.debug("Total issued connections: " + issuedConnections.size()); 
                      log.debug("Total allocated connection: " + numConnections + " out of " + maxTotalConnections);
                  }
                  
                  // the cases to check for:
                  // - have a free connection for that route
                  // - allowed to create a free connection for that route
                  // - can delete and replace a free connection for another route
                  // - need to wait for one of the things above to come true
  
                  entry = getFreeEntry(rospl, state);
                  if (entry != null) {
                      break;
                  }
                  
                  boolean hasCapacity = rospl.getCapacity() > 0; 
                  
                  if (log.isDebugEnabled()) {
                      log.debug("Available capacity: " + rospl.getCapacity() 
                              + " out of " + rospl.getMaxEntries()
                              + " [" + route + "][" + state + "]");
                  }
                  
                  if (hasCapacity && numConnections < maxTotalConnections) {
  
                      entry = createEntry(rospl, operator);
  
                  } else if (hasCapacity && !freeConnections.isEmpty()) {
  
                      deleteLeastUsedEntry();
                      entry = createEntry(rospl, operator);
  
                  } else {
  
                      ///M: Debugging logging for http connection blocking
                      System.out.println("[CDS][HTTP]Need to wait for connection" + " [" + route + "][" + state + "]");
  
                      if (log.isDebugEnabled()) {
                          log.debug("Need to wait for connection" +
  