View Javadoc

    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v 1.47 2004/12/21 11:27:55 olegk Exp $
    3    * $Revision: 564906 $
    4    * $Date: 2007-08-11 14:27:18 +0200 (Sat, 11 Aug 2007) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Licensed to the Apache Software Foundation (ASF) under one or more
    9    *  contributor license agreements.  See the NOTICE file distributed with
    10   *  this work for additional information regarding copyright ownership.
    11   *  The ASF licenses this file to You under the Apache License, Version 2.0
    12   *  (the "License"); you may not use this file except in compliance with
    13   *  the License.  You may obtain a copy of the License at
    14   *
    15   *      http://www.apache.org/licenses/LICENSE-2.0
    16   *
    17   *  Unless required by applicable law or agreed to in writing, software
    18   *  distributed under the License is distributed on an "AS IS" BASIS,
    19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    20   *  See the License for the specific language governing permissions and
    21   *  limitations under the License.
    22   * ====================================================================
    23   *
    24   * This software consists of voluntary contributions made by many
    25   * individuals on behalf of the Apache Software Foundation.  For more
    26   * information on the Apache Software Foundation, please see
    27   * <http://www.apache.org/>.
    28   *
    29   */
    30
    31  package org.apache.commons.httpclient;
    32
    33  import java.io.IOException;
    34  import java.io.InputStream;
    35  import java.io.OutputStream;
    36  import java.lang.ref.Reference;
    37  import java.lang.ref.ReferenceQueue;
    38  import java.lang.ref.WeakReference;
    39  import java.net.InetAddress;
    40  import java.net.SocketException;
    41  import java.util.ArrayList;
    42  import java.util.HashMap;
    43  import java.util.Iterator;
    44  import java.util.LinkedList;
    45  import java.util.Map;
    46  import java.util.WeakHashMap;
    47
    48  import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
    49  import org.apache.commons.httpclient.params.HttpConnectionParams;
    50  import org.apache.commons.httpclient.protocol.Protocol;
    51  import org.apache.commons.httpclient.util.IdleConnectionHandler;
    52  import org.apache.commons.logging.Log;
    53  import org.apache.commons.logging.LogFactory;
    54
    55  /**
    56   * Manages a set of HttpConnections for various HostConfigurations.
    57   *
    58   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
    59   * @author Eric Johnson
    60   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
    61   * @author Carl A. Dunham
    62   *
    63   * @since 2.0
    64   */
    65  public class MultiThreadedHttpConnectionManager implements HttpConnectionManager {
    66
    67      // -------------------------------------------------------- Class Variables
    68
    69      /** Log object for this class. */
    70      private static final Log LOG = LogFactory.getLog(MultiThreadedHttpConnectionManager.class);
    71
    72      /** The default maximum number of connections allowed per host */
    73      public static final int DEFAULT_MAX_HOST_CONNECTIONS = 2;   // Per RFC 2616 sec 8.1.4
    74
    75      /** The default maximum number of connections allowed overall */
    76      public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;
    77
    78      /**
    79       * A mapping from Reference to ConnectionSource.  Used to reclaim resources when connections
    80       * are lost to the garbage collector.
    81       */
    82      private static final Map REFERENCE_TO_CONNECTION_SOURCE = new HashMap();
    83
    84      /**
    85       * The reference queue used to track when HttpConnections are lost to the
    86       * garbage collector
    87       */
    88      private static final ReferenceQueue REFERENCE_QUEUE = new ReferenceQueue();
    89
    90      /**
    91       * The thread responsible for handling lost connections.
    92       */
    93      private static ReferenceQueueThread REFERENCE_QUEUE_THREAD;
    94
    95      /**
    96       * Holds references to all active instances of this class.
    97       */
    98      private static WeakHashMap ALL_CONNECTION_MANAGERS = new WeakHashMap();
    99
    100
    101     // ---------------------------------------------------------- Class Methods
    102
    103     /**
    104      * Shuts down and cleans up resources used by all instances of
    105      * MultiThreadedHttpConnectionManager. All static resources are released, all threads are
    106      * stopped, and {@link #shutdown()} is called on all live instances of
    107      * MultiThreadedHttpConnectionManager.
    108      *
    109      * @see #shutdown()
    110      */
    111     public static void shutdownAll() {
    112
    113         synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
    114             // shutdown all connection managers
    115             synchronized (ALL_CONNECTION_MANAGERS) {
    116                 // Don't use an iterator here. Iterators on WeakHashMap can
    117                 // get ConcurrentModificationException on garbage collection.
    118                 MultiThreadedHttpConnectionManager[]
    119                     connManagers = (MultiThreadedHttpConnectionManager[])
    120                     ALL_CONNECTION_MANAGERS.keySet().toArray(
    121                         new MultiThreadedHttpConnectionManager
    122                             [ALL_CONNECTION_MANAGERS.size()]
    123                         );
    124
    125                 // The map may shrink after size() is called, or some entry
    126                 // may get GCed while the array is built, so expect null.
    127                 for (int i=0; i<connManagers.length; i++) {
    128                     if (connManagers[i] != null)
    129                         connManagers[i].shutdown();
    130                 }
    131             }
    132
    133             // shutdown static resources
    134             if (REFERENCE_QUEUE_THREAD != null) {
    135                 REFERENCE_QUEUE_THREAD.shutdown();
    136                 REFERENCE_QUEUE_THREAD = null;
    137             }
    138             REFERENCE_TO_CONNECTION_SOURCE.clear();
    139         }
    140     }
    141
    142     /**
    143      * Stores the reference to the given connection along with the host config and connection pool.
    144      * These values will be used to reclaim resources if the connection is lost to the garbage
    145      * collector.  This method should be called before a connection is released from the connection
    146      * manager.
    147      *
    148      * <p>A static reference to the connection manager will also be stored.  To ensure that
    149      * the connection manager can be GCed {@link #removeReferenceToConnection(HttpConnection)}
    150      * should be called for all connections that the connection manager is storing a reference
    151      * to.</p>
    152      *
    153      * @param connection the connection to create a reference for
    154      * @param hostConfiguration the connection's host config
    155      * @param connectionPool the connection pool that created the connection
    156      *
    157      * @see #removeReferenceToConnection(HttpConnection)
    158      */
    159     private static void storeReferenceToConnection(
    160         HttpConnectionWithReference connection,
    161         HostConfiguration hostConfiguration,
    162         ConnectionPool connectionPool
    163     ) {
    164
    165         ConnectionSource source = new ConnectionSource();
    166         source.connectionPool = connectionPool;
    167         source.hostConfiguration = hostConfiguration;
    168
    169         synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
    170
    171             // start the reference queue thread if needed
    172             if (REFERENCE_QUEUE_THREAD == null) {
    173                 REFERENCE_QUEUE_THREAD = new ReferenceQueueThread();
    174                 REFERENCE_QUEUE_THREAD.start();
    175             }
    176
    177             REFERENCE_TO_CONNECTION_SOURCE.put(
    178                 connection.reference,
    179                 source
    180             );
    181         }
    182     }
    183
    184     /**
    185      * Closes and releases all connections currently checked out of the given connection pool.
    186      * @param connectionPool the connection pool to shutdown the connections for
    187      */
    188     private static void shutdownCheckedOutConnections(ConnectionPool connectionPool) {
    189
    190         // keep a list of the connections to be closed
    191         ArrayList connectionsToClose = new ArrayList();
    192
    193         synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
    194
    195             Iterator referenceIter = REFERENCE_TO_CONNECTION_SOURCE.keySet().iterator();
    196             while (referenceIter.hasNext()) {
    197                 Reference ref = (Reference) referenceIter.next();
    198                 ConnectionSource source =
    199                     (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.get(ref);
    200                 if (source.connectionPool == connectionPool) {
    201                     referenceIter.remove();
    202                     HttpConnection connection = (HttpConnection) ref.get();
    203                     if (connection != null) {
    204                         connectionsToClose.add(connection);
    205                     }
    206                 }
    207             }
    208         }
    209
    210         // close and release the connections outside of the synchronized block to
    211         // avoid holding the lock for too long
    212         for (Iterator i = connectionsToClose.iterator(); i.hasNext();) {
    213             HttpConnection connection = (HttpConnection) i.next();
    214             connection.close();
    215             // remove the reference to the connection manager. this ensures
    216             // that the we don't accidentally end up here again
    217             connection.setHttpConnectionManager(null);
    218             connection.releaseConnection();
    219         }
    220     }
    221
    222     /**
    223      * Removes the reference being stored for the given connection.  This method should be called
    224      * when the connection manager again has a direct reference to the connection.
    225      *
    226      * @param connection the connection to remove the reference for
    227      *
    228      * @see #storeReferenceToConnection(HttpConnection, HostConfiguration, ConnectionPool)
    229      */
    230     private static void removeReferenceToConnection(HttpConnectionWithReference connection) {
    231
    232         synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
    233             REFERENCE_TO_CONNECTION_SOURCE.remove(connection.reference);
    234         }
    235     }
    236
    237
    238     // ----------------------------------------------------- Instance Variables
    239
    240     /**
    241      * Collection of parameters associated with this connection manager.
    242      */
    243     private HttpConnectionManagerParams params = new HttpConnectionManagerParams();
    244
    245     /** Connection Pool */
    246     private ConnectionPool connectionPool;
    247
    248     private volatile boolean shutdown = false;
    249
    250
    251     // ----------------------------------------------------------- Constructors
    252
    253     /**
    254      * No-args constructor
    255      */
    256     public MultiThreadedHttpConnectionManager() {
    257         this.connectionPool = new ConnectionPool();
    258         synchronized(ALL_CONNECTION_MANAGERS) {
    259             ALL_CONNECTION_MANAGERS.put(this, null);
    260         }
    261     }
    262
    263
    264     // ------------------------------------------------------- Instance Methods
    265
    266     /**
    267      * Shuts down the connection manager and releases all resources.  All connections associated
    268      * with this class will be closed and released.
    269      *
    270      * <p>The connection manager can no longer be used once shut down.
    271      *
    272      * <p>Calling this method more than once will have no effect.
    273      */
    274     public synchronized void shutdown() {
    275         synchronized (connectionPool) {
    276             if (!shutdown) {
    277                 shutdown = true;
    278                 connectionPool.shutdown();
    279             }
    280         }
    281     }
    282
    283     /**
    284      * Gets the staleCheckingEnabled value to be set on HttpConnections that are created.
    285      *
    286      * @return <code>true</code> if stale checking will be enabled on HttpConnections
    287      *
    288      * @see HttpConnection#isStaleCheckingEnabled()
    289      *
    290      * @deprecated Use {@link HttpConnectionManagerParams#isStaleCheckingEnabled()},
    291      * {@link HttpConnectionManager#getParams()}.
    292      */
    293     public boolean isConnectionStaleCheckingEnabled() {
    294         return this.params.isStaleCheckingEnabled();
    295     }
    296
    297     /**
    298      * Sets the staleCheckingEnabled value to be set on HttpConnections that are created.
    299      *
    300      * @param connectionStaleCheckingEnabled <code>true</code> if stale checking will be enabled
    301      * on HttpConnections
    302      *
    303      * @see HttpConnection#setStaleCheckingEnabled(boolean)
    304      *
    305      * @deprecated Use {@link HttpConnectionManagerParams#setStaleCheckingEnabled(boolean)},
    306      * {@link HttpConnectionManager#getParams()}.
    307      */
    308     public void setConnectionStaleCheckingEnabled(boolean connectionStaleCheckingEnabled) {
    309         this.params.setStaleCheckingEnabled(connectionStaleCheckingEnabled);
    310     }
    311
    312     /**
    313      * Sets the maximum number of connections allowed for a given
    314      * HostConfiguration. Per RFC 2616 section 8.1.4, this value defaults to 2.
    315      *
    316      * @param maxHostConnections the number of connections allowed for each
    317      * hostConfiguration
    318      *
    319      * @deprecated Use {@link HttpConnectionManagerParams#setDefaultMaxConnectionsPerHost(int)},
    320      * {@link HttpConnectionManager#getParams()}.
    321      */
    322     public void setMaxConnectionsPerHost(int maxHostConnections) {
    323         this.params.setDefaultMaxConnectionsPerHost(maxHostConnections);
    324     }
    325
    326     /**
    327      * Gets the maximum number of connections allowed for a given
    328      * hostConfiguration.
    329      *
    330      * @return The maximum number of connections allowed for a given
    331      * hostConfiguration.
    332      *
    333      * @deprecated Use {@link HttpConnectionManagerParams#getDefaultMaxConnectionsPerHost()},
    334      * {@link HttpConnectionManager#getParams()}.
    335      */
    336     public int getMaxConnectionsPerHost() {
    337         return this.params.getDefaultMaxConnectionsPerHost();
    338     }
    339
    340     /**
    341      * Sets the maximum number of connections allowed for this connection manager.
    342      *
    343      * @param maxTotalConnections the maximum number of connections allowed
    344      *
    345      * @deprecated Use {@link HttpConnectionManagerParams#setMaxTotalConnections(int)},
    346      * {@link HttpConnectionManager#getParams()}.
    347      */
    348     public void setMaxTotalConnections(int maxTotalConnections) {
    349         this.params.setMaxTotalConnections(maxTotalConnections);
    350     }
    351
    352     /**
    353      * Gets the maximum number of connections allowed for this connection manager.
    354      *
    355      * @return The maximum number of connections allowed
    356      *
    357      * @deprecated Use {@link HttpConnectionManagerParams#getMaxTotalConnections()},
    358      * {@link HttpConnectionManager#getParams()}.
    359      */
    360     public int getMaxTotalConnections() {
    361         return this.params.getMaxTotalConnections();
    362     }
    363
    364     /**
    365      * @see HttpConnectionManager#getConnection(HostConfiguration)
    366      */
    367     public HttpConnection getConnection(HostConfiguration hostConfiguration) {
    368
    369         while (true) {
    370             try {
    371                 return getConnectionWithTimeout(hostConfiguration, 0);
    372             } catch (ConnectionPoolTimeoutException e) {
    373                 // we'll go ahead and log this, but it should never happen. HttpExceptions
    374                 // are only thrown when the timeout occurs and since we have no timeout
    375                 // it should never happen.
    376                 LOG.debug(
    377                     "Unexpected exception while waiting for connection",
    378                     e
    379                 );
    380             }
    381         }
    382     }
    383
    384     /**
    385      * Gets a connection or waits if one is not available.  A connection is
    386      * available if one exists that is not being used or if fewer than
    387      * maxHostConnections have been created in the connectionPool, and fewer
    388      * than maxTotalConnections have been created in all connectionPools.
    389      *
    390      * @param hostConfiguration The host configuration specifying the connection
    391      *        details.
    392      * @param timeout the number of milliseconds to wait for a connection, 0 to
    393      * wait indefinitely
    394      *
    395      * @return HttpConnection an available connection
    396      *
    397      * @throws HttpException if a connection does not become available in
    398      * 'timeout' milliseconds
    399      *
    400      * @since 3.0
    401      */
    402     public HttpConnection getConnectionWithTimeout(HostConfiguration hostConfiguration,
    403         long timeout) throws ConnectionPoolTimeoutException {
    404
    405         LOG.trace("enter HttpConnectionManager.getConnectionWithTimeout(HostConfiguration, long)");
    406
    407         if (hostConfiguration == null) {
    408             throw new IllegalArgumentException("hostConfiguration is null");
    409         }
    410
    411         if (LOG.isDebugEnabled()) {
    412             LOG.debug("HttpConnectionManager.getConnection:  config = "
    413                 + hostConfiguration + ", timeout = " + timeout);
    414         }
    415
    416         final HttpConnection conn = doGetConnection(hostConfiguration, timeout);
    417
    418         // wrap the connection in an adapter so we can ensure it is used
    419         // only once
    420         return new HttpConnectionAdapter(conn);
    421     }
    422
    423 	/**
    424 	 * @see HttpConnectionManager#getConnection(HostConfiguration, long)
    425 	 *
    426 	 * @deprecated Use #getConnectionWithTimeout(HostConfiguration, long)
    427 	 */
    428 	public HttpConnection getConnection(HostConfiguration hostConfiguration,
    429 		long timeout) throws HttpException {
    430
    431 		LOG.trace("enter HttpConnectionManager.getConnection(HostConfiguration, long)");
    432 		try {
    433 			return getConnectionWithTimeout(hostConfiguration, timeout);
    434 		} catch(ConnectionPoolTimeoutException e) {
    435 			throw new HttpException(e.getMessage());
    436 		}
    437 	}
    438
    439     private HttpConnection doGetConnection(HostConfiguration hostConfiguration,
    440         long timeout) throws ConnectionPoolTimeoutException {
    441
    442         HttpConnection connection = null;
    443
    444         int maxHostConnections = this.params.getMaxConnectionsPerHost(hostConfiguration);
    445         int maxTotalConnections = this.params.getMaxTotalConnections();
    446
    447         synchronized (connectionPool) {
    448
    449             // we clone the hostConfiguration
    450             // so that it cannot be changed once the connection has been retrieved
    451             hostConfiguration = new HostConfiguration(hostConfiguration);
    452             HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration, true);
    453             WaitingThread waitingThread = null;
    454
    455             boolean useTimeout = (timeout > 0);
    456             long timeToWait = timeout;
    457             long startWait = 0;
    458             long endWait = 0;
    459
    460             while (connection == null) {
    461
    462                 if (shutdown) {
    463                     throw new IllegalStateException("Connection factory has been shutdown.");
    464                 }
    465
    466                 // happen to have a free connection with the right specs
    467                 //
    468                 if (hostPool.freeConnections.size() > 0) {
    469                     connection = connectionPool.getFreeConnection(hostConfiguration);
    470
    471                 // have room to make more
    472                 //
    473                 } else if ((hostPool.numConnections < maxHostConnections)
    474                     && (connectionPool.numConnections < maxTotalConnections)) {
    475
    476                     connection = connectionPool.createConnection(hostConfiguration);
    477
    478                 // have room to add host connection, and there is at least one free
    479                 // connection that can be liberated to make overall room
    480                 //
    481                 } else if ((hostPool.numConnections < maxHostConnections)
    482                     && (connectionPool.freeConnections.size() > 0)) {
    483
    484                     connectionPool.deleteLeastUsedConnection();
    485                     connection = connectionPool.createConnection(hostConfiguration);
    486
    487                 // otherwise, we have to wait for one of the above conditions to
    488                 // become true
    489                 //
    490                 } else {
    491                     // TODO: keep track of which hostConfigurations have waiting
    492                     // threads, so they avoid being sacrificed before necessary
    493
    494                     try {
    495
    496                         if (useTimeout && timeToWait <= 0) {
    497                             throw new ConnectionPoolTimeoutException("Timeout waiting for connection");
    498                         }
    499
    500                         if (LOG.isDebugEnabled()) {
    501                             LOG.debug("Unable to get a connection, waiting..., hostConfig=" + hostConfiguration);
    502                         }
    503
    504                         if (waitingThread == null) {
    505                             waitingThread = new WaitingThread();
    506                             waitingThread.hostConnectionPool = hostPool;
    507                             waitingThread.thread = Thread.currentThread();
    508                         } else {
    509                             waitingThread.interruptedByConnectionPool = false;
    510                         }
    511
    512                         if (useTimeout) {
    513                             startWait = System.currentTimeMillis();
    514                         }
    515
    516                         hostPool.waitingThreads.addLast(waitingThread);
    517                         connectionPool.waitingThreads.addLast(waitingThread);
    518                         connectionPool.wait(timeToWait);
    519                     } catch (InterruptedException e) {
    520                         if (!waitingThread.interruptedByConnectionPool) {
    521                             LOG.debug("Interrupted while waiting for connection", e);
    522                             throw new IllegalThreadStateException(
    523                                 "Interrupted while waiting in MultiThreadedHttpConnectionManager");
    524                         }
    525                         // Else, do nothing, we were interrupted by the connection pool
    526                         // and should now have a connection waiting for us, continue
    527                         // in the loop and let's get it.
    528                     } finally {
    529                         if (!waitingThread.interruptedByConnectionPool) {
    530                             // Either we timed out, experienced a "spurious wakeup", or were
    531                             // interrupted by an external thread.  Regardless we need to
    532                             // cleanup for ourselves in the wait queue.
    533                             hostPool.waitingThreads.remove(waitingThread);
    534                             connectionPool.waitingThreads.remove(waitingThread);
    535                         }
    536
    537                         if (useTimeout) {
    538                             endWait = System.currentTimeMillis();
    539                             timeToWait -= (endWait - startWait);
    540                         }
    541                     }
    542                 }
    543             }
    544         }
    545         return connection;
    546     }
    547
    548     /**
    549      * Gets the total number of pooled connections for the given host configuration.  This
    550      * is the total number of connections that have been created and are still in use
    551      * by this connection manager for the host configuration.  This value will
    552      * not exceed the {@link #getMaxConnectionsPerHost() maximum number of connections per
    553      * host}.
    554      *
    555      * @param hostConfiguration The host configuration
    556      * @return The total number of pooled connections
    557      */
    558     public int getConnectionsInPool(HostConfiguration hostConfiguration) {
    559         synchronized (connectionPool) {
    560             HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration, false);
    561             return (hostPool != null) ? hostPool.numConnections : 0;
    562         }
    563     }
    564
    565     /**
    566      * Gets the total number of pooled connections.  This is the total number of
    567      * connections that have been created and are still in use by this connection
    568      * manager.  This value will not exceed the {@link #getMaxTotalConnections()
    569      * maximum number of connections}.
    570      *
    571      * @return the total number of pooled connections
    572      */
    573     public int getConnectionsInPool() {
    574         synchronized (connectionPool) {
    575             return connectionPool.numConnections;
    576         }
    577     }
    578
    579     /**
    580      * Gets the number of connections in use for this configuration.
    581      *
    582      * @param hostConfiguration the key that connections are tracked on
    583      * @return the number of connections in use
    584      *
    585      * @deprecated Use {@link #getConnectionsInPool(HostConfiguration)}
    586      */
    587     public int getConnectionsInUse(HostConfiguration hostConfiguration) {
    588         return getConnectionsInPool(hostConfiguration);
    589     }
    590
    591     /**
    592      * Gets the total number of connections in use.
    593      *
    594      * @return the total number of connections in use
    595      *
    596      * @deprecated Use {@link #getConnectionsInPool()}
    597      */
    598     public int getConnectionsInUse() {
    599         return getConnectionsInPool();
    600     }
    601
    602     /**
    603      * Deletes all closed connections.  Only connections currently owned by the connection
    604      * manager are processed.
    605      *
    606      * @see HttpConnection#isOpen()
    607      *
    608      * @since 3.0
    609      */
    610     public void deleteClosedConnections() {
    611         connectionPool.deleteClosedConnections();
    612     }
    613
    614     /**
    615      * @since 3.0
    616      */
    617     public void closeIdleConnections(long idleTimeout) {
    618         connectionPool.closeIdleConnections(idleTimeout);
    619         deleteClosedConnections();
    620     }
    621
    622     /**
    623      * Make the given HttpConnection available for use by other requests.
    624      * If another thread is blocked in getConnection() that could use this
    625      * connection, it will be woken up.
    626      *
    627      * @param conn the HttpConnection to make available.
    628      */
    629     public void releaseConnection(HttpConnection conn) {
    630         LOG.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
    631
    632         if (conn instanceof HttpConnectionAdapter) {
    633             // connections given out are wrapped in an HttpConnectionAdapter
    634             conn = ((HttpConnectionAdapter) conn).getWrappedConnection();
    635         } else {
    636             // this is okay, when an HttpConnectionAdapter is released
    637             // is releases the real connection
    638         }
    639
    640         // make sure that the response has been read.
    641         SimpleHttpConnectionManager.finishLastResponse(conn);
    642
    643         connectionPool.freeConnection(conn);
    644     }
    645
    646     /**
    647      * Gets the host configuration for a connection.
    648      * @param conn the connection to get the configuration of
    649      * @return a new HostConfiguration
    650      */
    651     private HostConfiguration configurationForConnection(HttpConnection conn) {
    652
    653         HostConfiguration connectionConfiguration = new HostConfiguration();
    654
    655         connectionConfiguration.setHost(
    656             conn.getHost(),
    657             conn.getPort(),
    658             conn.getProtocol()
    659         );
    660         if (conn.getLocalAddress() != null) {
    661             connectionConfiguration.setLocalAddress(conn.getLocalAddress());
    662         }
    663         if (conn.getProxyHost() != null) {
    664             connectionConfiguration.setProxy(conn.getProxyHost(), conn.getProxyPort());
    665         }
    666
    667         return connectionConfiguration;
    668     }
    669
    670     /**
    671      * Returns {@link HttpConnectionManagerParams parameters} associated
    672      * with this connection manager.
    673      *
    674      * @since 3.0
    675      *
    676      * @see HttpConnectionManagerParams
    677      */
    678     public HttpConnectionManagerParams getParams() {
    679         return this.params;
    680     }
    681
    682     /**
    683      * Assigns {@link HttpConnectionManagerParams parameters} for this
    684      * connection manager.
    685      *
    686      * @since 3.0
    687      *
    688      * @see HttpConnectionManagerParams
    689      */
    690     public void setParams(final HttpConnectionManagerParams params) {
    691         if (params == null) {
    692             throw new IllegalArgumentException("Parameters may not be null");
    693         }
    694         this.params = params;
    695     }
    696
    697     /**
    698      * Global Connection Pool, including per-host pools
    699      */
    700     private class ConnectionPool {
    701
    702         /** The list of free connections */
    703         private LinkedList freeConnections = new LinkedList();
    704
    705         /** The list of WaitingThreads waiting for a connection */
    706         private LinkedList waitingThreads = new LinkedList();
    707
    708         /**
    709          * Map where keys are {@link HostConfiguration}s and values are {@link
    710          * HostConnectionPool}s
    711          */
    712         private final Map mapHosts = new HashMap();
    713
    714         private IdleConnectionHandler idleConnectionHandler = new IdleConnectionHandler();
    715
    716         /** The number of created connections */
    717         private int numConnections = 0;
    718
    719         /**
    720          * Cleans up all connection pool resources.
    721          */
    722         public synchronized void shutdown() {
    723
    724             // close all free connections
    725             Iterator iter = freeConnections.iterator();
    726             while (iter.hasNext()) {
    727                 HttpConnection conn = (HttpConnection) iter.next();
    728                 iter.remove();
    729                 conn.close();
    730             }
    731
    732             // close all connections that have been checked out
    733             shutdownCheckedOutConnections(this);
    734
    735             // interrupt all waiting threads
    736             iter = waitingThreads.iterator();
    737             while (iter.hasNext()) {
    738                 WaitingThread waiter = (WaitingThread) iter.next();
    739                 iter.remove();
    740                 waiter.interruptedByConnectionPool = true;
    741                 waiter.thread.interrupt();
    742             }
    743
    744             // clear out map hosts
    745             mapHosts.clear();
    746
    747             // remove all references to connections
    748             idleConnectionHandler.removeAll();
    749         }
    750
    751         /**
    752          * Creates a new connection and returns it for use of the calling method.
    753          *
    754          * @param hostConfiguration the configuration for the connection
    755          * @return a new connection or <code>null</code> if none are available
    756          */
    757         public synchronized HttpConnection createConnection(HostConfiguration hostConfiguration) {
    758             HostConnectionPool hostPool = getHostPool(hostConfiguration, true);
    759             if (LOG.isDebugEnabled()) {
    760                 LOG.debug("Allocating new connection, hostConfig=" + hostConfiguration);
    761             }
    762             HttpConnectionWithReference connection = new HttpConnectionWithReference(
    763                     hostConfiguration);
    764             connection.getParams().setDefaults(MultiThreadedHttpConnectionManager.this.params);
    765             connection.setHttpConnectionManager(MultiThreadedHttpConnectionManager.this);
    766             numConnections++;
    767             hostPool.numConnections++;
    768
    769             // store a reference to this connection so that it can be cleaned up
    770             // in the event it is not correctly released
    771             storeReferenceToConnection(connection, hostConfiguration, this);
    772             return connection;
    773         }
    774
    775         /**
    776          * Handles cleaning up for a lost connection with the given config.  Decrements any
    777          * connection counts and notifies waiting threads, if appropriate.
    778          *
    779          * @param config the host configuration of the connection that was lost
    780          */
    781         public synchronized void handleLostConnection(HostConfiguration config) {
    782             HostConnectionPool hostPool = getHostPool(config, true);
    783             hostPool.numConnections--;
    784             if ((hostPool.numConnections == 0) &&
    785                 hostPool.waitingThreads.isEmpty()) {
    786
    787                 mapHosts.remove(config);
    788             }
    789
    790             numConnections--;
    791             notifyWaitingThread(config);
    792         }
    793
    794         /**
    795          * Get the pool (list) of connections available for the given hostConfig.
    796          *
    797          * @param hostConfiguration the configuraton for the connection pool
    798          * @param create <code>true</code> to create a pool if not found,
    799          *               <code>false</code> to return <code>null</code>
    800          *
    801          * @return a pool (list) of connections available for the given config,
    802          *         or <code>null</code> if neither found nor created
    803          */
    804         public synchronized HostConnectionPool getHostPool(HostConfiguration hostConfiguration, boolean create) {
    805             LOG.trace("enter HttpConnectionManager.ConnectionPool.getHostPool(HostConfiguration)");
    806
    807             // Look for a list of connections for the given config
    808             HostConnectionPool listConnections = (HostConnectionPool)
    809                 mapHosts.get(hostConfiguration);
    810             if ((listConnections == null) && create) {
    811                 // First time for this config
    812                 listConnections = new HostConnectionPool();
    813                 listConnections.hostConfiguration = hostConfiguration;
    814                 mapHosts.put(hostConfiguration, listConnections);
    815             }
    816
    817             return listConnections;
    818         }
    819
    820         /**
    821          * If available, get a free connection for this host
    822          *
    823          * @param hostConfiguration the configuraton for the connection pool
    824          * @return an available connection for the given config
    825          */
    826         public synchronized HttpConnection getFreeConnection(HostConfiguration hostConfiguration) {
    827
    828             HttpConnectionWithReference connection = null;
    829
    830             HostConnectionPool hostPool = getHostPool(hostConfiguration, false);
    831
    832             if ((hostPool != null) && (hostPool.freeConnections.size() > 0)) {
    833                 connection = (HttpConnectionWithReference) hostPool.freeConnections.removeLast();
    834                 freeConnections.remove(connection);
    835                 // store a reference to this connection so that it can be cleaned up
    836                 // in the event it is not correctly released
    837                 storeReferenceToConnection(connection, hostConfiguration, this);
    838                 if (LOG.isDebugEnabled()) {
    839                     LOG.debug("Getting free connection, hostConfig=" + hostConfiguration);
    840                 }
    841
    842                 // remove the connection from the timeout handler
    843                 idleConnectionHandler.remove(connection);
    844             } else if (LOG.isDebugEnabled()) {
    845                 LOG.debug("There were no free connections to get, hostConfig="
    846                     + hostConfiguration);
    847             }
    848             return connection;
    849         }
    850
    851         /**
    852          * Deletes all closed connections.
    853          */
    854         public synchronized void deleteClosedConnections() {
    855
    856             Iterator iter = freeConnections.iterator();
    857
    858             while (iter.hasNext()) {
    859                 HttpConnection conn = (HttpConnection) iter.next();
    860                 if (!conn.isOpen()) {
    861                     iter.remove();
    862                     deleteConnection(conn);
    863                 }
    864             }
    865         }
    866
    867         /**
    868          * Closes idle connections.
    869          * @param idleTimeout
    870          */
    871         public synchronized void closeIdleConnections(long idleTimeout) {
    872             idleConnectionHandler.closeIdleConnections(idleTimeout);
    873         }
    874
    875         /**
    876          * Deletes the given connection.  This will remove all reference to the connection
    877          * so that it can be GCed.
    878          *
    879          * <p><b>Note:</b> Does not remove the connection from the freeConnections list.  It
    880          * is assumed that the caller has already handled this case.</p>
    881          *
    882          * @param connection The connection to delete
    883          */
    884         private synchronized void deleteConnection(HttpConnection connection) {
    885
    886             HostConfiguration connectionConfiguration = configurationForConnection(connection);
    887
    888             if (LOG.isDebugEnabled()) {
    889                 LOG.debug("Reclaiming connection, hostConfig=" + connectionConfiguration);
    890             }
    891
    892             connection.close();
    893
    894             HostConnectionPool hostPool = getHostPool(connectionConfiguration, true);
    895
    896             hostPool.freeConnections.remove(connection);
    897             hostPool.numConnections--;
    898             numConnections--;
    899             if ((hostPool.numConnections == 0) &&
    900                 hostPool.waitingThreads.isEmpty()) {
    901
    902                 mapHosts.remove(connectionConfiguration);
    903             }
    904
    905             // remove the connection from the timeout handler
    906             idleConnectionHandler.remove(connection);
    907         }
    908
    909         /**
    910          * Close and delete an old, unused connection to make room for a new one.
    911          */
    912         public synchronized void deleteLeastUsedConnection() {
    913
    914             HttpConnection connection = (HttpConnection) freeConnections.removeFirst();
    915
    916             if (connection != null) {
    917                 deleteConnection(connection);
    918             } else if (LOG.isDebugEnabled()) {
    919                 LOG.debug("Attempted to reclaim an unused connection but there were none.");
    920             }
    921         }
    922
    923         /**
    924          * Notifies a waiting thread that a connection for the given configuration is
    925          * available.
    926          * @param configuration the host config to use for notifying
    927          * @see #notifyWaitingThread(HostConnectionPool)
    928          */
    929         public synchronized void notifyWaitingThread(HostConfiguration configuration) {
    930             notifyWaitingThread(getHostPool(configuration, true));
    931         }
    932
    933         /**
    934          * Notifies a waiting thread that a connection for the given configuration is
    935          * available.  This will wake a thread waiting in this host pool or if there is not
    936          * one a thread in the connection pool will be notified.
    937          *
    938          * @param hostPool the host pool to use for notifying
    939          */
    940         public synchronized void notifyWaitingThread(HostConnectionPool hostPool) {
    941
    942             // find the thread we are going to notify, we want to ensure that each
    943             // waiting thread is only interrupted once so we will remove it from
    944             // all wait queues before interrupting it
    945             WaitingThread waitingThread = null;
    946
    947             if (hostPool.waitingThreads.size() > 0) {
    948                 if (LOG.isDebugEnabled()) {
    949                     LOG.debug("Notifying thread waiting on host pool, hostConfig="
    950                         + hostPool.hostConfiguration);
    951                 }
    952                 waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
    953                 waitingThreads.remove(waitingThread);
    954             } else if (waitingThreads.size() > 0) {
    955                 if (LOG.isDebugEnabled()) {
    956                     LOG.debug("No-one waiting on host pool, notifying next waiting thread.");
    957                 }
    958                 waitingThread = (WaitingThread) waitingThreads.removeFirst();
    959                 waitingThread.hostConnectionPool.waitingThreads.remove(waitingThread);
    960             } else if (LOG.isDebugEnabled()) {
    961                 LOG.debug("Notifying no-one, there are no waiting threads");
    962             }
    963
    964             if (waitingThread != null) {
    965                 waitingThread.interruptedByConnectionPool = true;
    966                 waitingThread.thread.interrupt();
    967             }
    968         }
    969
    970         /**
    971          * Marks the given connection as free.
    972          * @param conn a connection that is no longer being used
    973          */
    974         public void freeConnection(HttpConnection conn) {
    975
    976             HostConfiguration connectionConfiguration = configurationForConnection(conn);
    977
    978             if (LOG.isDebugEnabled()) {
    979                 LOG.debug("Freeing connection, hostConfig=" + connectionConfiguration);
    980             }
    981
    982             synchronized (this) {
    983
    984                 if (shutdown) {
    985                     // the connection manager has been shutdown, release the connection's
    986                     // resources and get out of here
    987                     conn.close();
    988                     return;
    989                 }
    990
    991                 HostConnectionPool hostPool = getHostPool(connectionConfiguration, true);
    992
    993                 // Put the connect back in the available list and notify a waiter
    994                 hostPool.freeConnections.add(conn);
    995                 if (hostPool.numConnections == 0) {
    996                     // for some reason this connection pool didn't already exist
    997                     LOG.error("Host connection pool not found, hostConfig="
    998                               + connectionConfiguration);
    999                     hostPool.numConnections = 1;
    1000                 }
    1001
    1002                 freeConnections.add(conn);
    1003                 // we can remove the reference to this connection as we have control over
    1004                 // it again.  this also ensures that the connection manager can be GCed
    1005                 removeReferenceToConnection((HttpConnectionWithReference) conn);
    1006                 if (numConnections == 0) {
    1007                     // for some reason this connection pool didn't already exist
    1008                     LOG.error("Host connection pool not found, hostConfig="
    1009                               + connectionConfiguration);
    1010                     numConnections = 1;
    1011                 }
    1012
    1013                 // register the connection with the timeout handler
    1014                 idleConnectionHandler.add(conn);
    1015
    1016                 notifyWaitingThread(hostPool);
    1017             }
    1018         }
    1019     }
    1020
    1021     /**
    1022      * A simple struct-like class to combine the objects needed to release a connection's
    1023      * resources when claimed by the garbage collector.
    1024      */
    1025     private static class ConnectionSource {
    1026
    1027         /** The connection pool that created the connection */
    1028         public ConnectionPool connectionPool;
    1029
    1030         /** The connection's host configuration */
    1031         public HostConfiguration hostConfiguration;
    1032     }
    1033
    1034     /**
    1035      * A simple struct-like class to combine the connection list and the count
    1036      * of created connections.
    1037      */
    1038     private static class HostConnectionPool {
    1039         /** The hostConfig this pool is for */
    1040         public HostConfiguration hostConfiguration;
    1041
    1042         /** The list of free connections */
    1043         public LinkedList freeConnections = new LinkedList();
    1044
    1045         /** The list of WaitingThreads for this host */
    1046         public LinkedList waitingThreads = new LinkedList();
    1047
    1048         /** The number of created connections */
    1049         public int numConnections = 0;
    1050     }
    1051
    1052     /**
    1053      * A simple struct-like class to combine the waiting thread and the connection
    1054      * pool it is waiting on.
    1055      */
    1056     private static class WaitingThread {
    1057         /** The thread that is waiting for a connection */
    1058         public Thread thread;
    1059
    1060         /** The connection pool the thread is waiting for */
    1061         public HostConnectionPool hostConnectionPool;
    1062
    1063         /** Flag to indicate if the thread was interrupted by the ConnectionPool. Set
    1064          * to true inside {@link ConnectionPool#notifyWaitingThread(HostConnectionPool)}
    1065          * before the thread is interrupted. */
    1066         public boolean interruptedByConnectionPool = false;
    1067     }
    1068
    1069     /**
    1070      * A thread for listening for HttpConnections reclaimed by the garbage
    1071      * collector.
    1072      */
    1073     private static class ReferenceQueueThread extends Thread {
    1074
    1075         private volatile boolean shutdown = false;
    1076
    1077         /**
    1078          * Create an instance and make this a daemon thread.
    1079          */
    1080         public ReferenceQueueThread() {
    1081             setDaemon(true);
    1082             setName("MultiThreadedHttpConnectionManager cleanup");
    1083         }
    1084
    1085         public void shutdown() {
    1086             this.shutdown = true;
    1087             this.interrupt();
    1088         }
    1089
    1090         /**
    1091          * Handles cleaning up for the given connection reference.
    1092          *
    1093          * @param ref the reference to clean up
    1094          */
    1095         private void handleReference(Reference ref) {
    1096
    1097             ConnectionSource source = null;
    1098
    1099             synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
    1100                 source = (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.remove(ref);
    1101             }
    1102             // only clean up for this reference if it is still associated with
    1103             // a ConnectionSource
    1104             if (source != null) {
    1105                 if (LOG.isDebugEnabled()) {
    1106                     LOG.debug(
    1107                         "Connection reclaimed by garbage collector, hostConfig="
    1108                         + source.hostConfiguration);
    1109                 }
    1110
    1111                 source.connectionPool.handleLostConnection(source.hostConfiguration);
    1112             }
    1113         }
    1114
    1115         /**
    1116          * Start execution.
    1117          */
    1118         public void run() {
    1119             while (!shutdown) {
    1120                 try {
    1121                     // remove the next reference and process it
    1122                     Reference ref = REFERENCE_QUEUE.remove();
    1123                     if (ref != null) {
    1124                         handleReference(ref);
    1125                     }
    1126                 } catch (InterruptedException e) {
    1127                     LOG.debug("ReferenceQueueThread interrupted", e);
    1128                 }
    1129             }
    1130         }
    1131
    1132     }
    1133
    1134     /**
    1135      * A connection that keeps a reference to itself.
    1136      */
    1137     private static class HttpConnectionWithReference extends HttpConnection {
    1138
    1139         public WeakReference reference = new WeakReference(this, REFERENCE_QUEUE);
    1140
    1141         /**
    1142          * @param hostConfiguration
    1143          */
    1144         public HttpConnectionWithReference(HostConfiguration hostConfiguration) {
    1145             super(hostConfiguration);
    1146         }
    1147
    1148     }
    1149
    1150     /**
    1151      * An HttpConnection wrapper that ensures a connection cannot be used
    1152      * once released.
    1153      */
    1154     private static class HttpConnectionAdapter extends HttpConnection {
    1155
    1156         // the wrapped connection
    1157         private HttpConnection wrappedConnection;
    1158
    1159         /**
    1160          * Creates a new HttpConnectionAdapter.
    1161          * @param connection the connection to be wrapped
    1162          */
    1163         public HttpConnectionAdapter(HttpConnection connection) {
    1164             super(connection.getHost(), connection.getPort(), connection.getProtocol());
    1165             this.wrappedConnection = connection;
    1166         }
    1167
    1168         /**
    1169          * Tests if the wrapped connection is still available.
    1170          * @return boolean
    1171          */
    1172         protected boolean hasConnection() {
    1173             return wrappedConnection != null;
    1174         }
    1175
    1176         /**
    1177          * @return HttpConnection
    1178          */
    1179         HttpConnection getWrappedConnection() {
    1180             return wrappedConnection;
    1181         }
    1182
    1183         public void close() {
    1184             if (hasConnection()) {
    1185                 wrappedConnection.close();
    1186             } else {
    1187                 // do nothing
    1188             }
    1189         }
    1190
    1191         public InetAddress getLocalAddress() {
    1192             if (hasConnection()) {
    1193                 return wrappedConnection.getLocalAddress();
    1194             } else {
    1195                 return null;
    1196             }
    1197         }
    1198
    1199         /**
    1200          * @deprecated
    1201          */
    1202         public boolean isStaleCheckingEnabled() {
    1203             if (hasConnection()) {
    1204                 return wrappedConnection.isStaleCheckingEnabled();
    1205             } else {
    1206                 return false;
    1207             }
    1208         }
    1209
    1210         public void setLocalAddress(InetAddress localAddress) {
    1211             if (hasConnection()) {
    1212                 wrappedConnection.setLocalAddress(localAddress);
    1213             } else {
    1214                 throw new IllegalStateException("Connection has been released");
    1215             }
    1216         }
    1217
    1218         /**
    1219          * @deprecated
    1220          */
    1221         public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
    1222             if (hasConnection()) {
    1223                 wrappedConnection.setStaleCheckingEnabled(staleCheckEnabled);
    1224             } else {
    1225                 throw new IllegalStateException("Connection has been released");
    1226             }
    1227         }
    1228
    1229         public String getHost() {
    1230             if (hasConnection()) {
    1231                 return wrappedConnection.getHost();
    1232             } else {
    1233                 return null;
    1234             }
    1235         }
    1236
    1237         public HttpConnectionManager getHttpConnectionManager() {
    1238             if (hasConnection()) {
    1239                 return wrappedConnection.getHttpConnectionManager();
    1240             } else {
    1241                 return null;
    1242             }
    1243         }
    1244
    1245         public InputStream getLastResponseInputStream() {
    1246             if (hasConnection()) {
    1247                 return wrappedConnection.getLastResponseInputStream();
    1248             } else {
    1249                 return null;
    1250             }
    1251         }
    1252
    1253         public int getPort() {
    1254             if (hasConnection()) {
    1255                 return wrappedConnection.getPort();
    1256             } else {
    1257                 return -1;
    1258             }
    1259         }
    1260
    1261         public Protocol getProtocol() {
    1262             if (hasConnection()) {
    1263                 return wrappedConnection.getProtocol();
    1264             } else {
    1265                 return null;
    1266             }
    1267         }
    1268
    1269         public String getProxyHost() {
    1270             if (hasConnection()) {
    1271                 return wrappedConnection.getProxyHost();
    1272             } else {
    1273                 return null;
    1274             }
    1275         }
    1276
    1277         public int getProxyPort() {
    1278             if (hasConnection()) {
    1279                 return wrappedConnection.getProxyPort();
    1280             } else {
    1281                 return -1;
    1282             }
    1283         }
    1284
    1285         public OutputStream getRequestOutputStream()
    1286             throws IOException, IllegalStateException {
    1287             if (hasConnection()) {
    1288                 return wrappedConnection.getRequestOutputStream();
    1289             } else {
    1290                 return null;
    1291             }
    1292         }
    1293
    1294         public InputStream getResponseInputStream()
    1295             throws IOException, IllegalStateException {
    1296             if (hasConnection()) {
    1297                 return wrappedConnection.getResponseInputStream();
    1298             } else {
    1299                 return null;
    1300             }
    1301         }
    1302
    1303         public boolean isOpen() {
    1304             if (hasConnection()) {
    1305                 return wrappedConnection.isOpen();
    1306             } else {
    1307                 return false;
    1308             }
    1309         }
    1310
    1311         public boolean closeIfStale() throws IOException {
    1312             if (hasConnection()) {
    1313                 return wrappedConnection.closeIfStale();
    1314             } else {
    1315                 return false;
    1316             }
    1317         }
    1318
    1319         public boolean isProxied() {
    1320             if (hasConnection()) {
    1321                 return wrappedConnection.isProxied();
    1322             } else {
    1323                 return false;
    1324             }
    1325         }
    1326
    1327         public boolean isResponseAvailable() throws IOException {
    1328             if (hasConnection()) {
    1329                 return  wrappedConnection.isResponseAvailable();
    1330             } else {
    1331                 return false;
    1332             }
    1333         }
    1334
    1335         public boolean isResponseAvailable(int timeout) throws IOException {
    1336             if (hasConnection()) {
    1337                 return  wrappedConnection.isResponseAvailable(timeout);
    1338             } else {
    1339                 return false;
    1340             }
    1341         }
    1342
    1343         public boolean isSecure() {
    1344             if (hasConnection()) {
    1345                 return wrappedConnection.isSecure();
    1346             } else {
    1347                 return false;
    1348             }
    1349         }
    1350
    1351         public boolean isTransparent() {
    1352             if (hasConnection()) {
    1353                 return wrappedConnection.isTransparent();
    1354             } else {
    1355                 return false;
    1356             }
    1357         }
    1358
    1359         public void open() throws IOException {
    1360             if (hasConnection()) {
    1361                 wrappedConnection.open();
    1362             } else {
    1363                 throw new IllegalStateException("Connection has been released");
    1364             }
    1365         }
    1366
    1367         /**
    1368          * @deprecated
    1369          */
    1370         public void print(String data)
    1371             throws IOException, IllegalStateException {
    1372             if (hasConnection()) {
    1373                 wrappedConnection.print(data);
    1374             } else {
    1375                 throw new IllegalStateException("Connection has been released");
    1376             }
    1377         }
    1378
    1379         public void printLine()
    1380             throws IOException, IllegalStateException {
    1381             if (hasConnection()) {
    1382                 wrappedConnection.printLine();
    1383             } else {
    1384                 throw new IllegalStateException("Connection has been released");
    1385             }
    1386         }
    1387
    1388         /**
    1389          * @deprecated
    1390          */
    1391         public void printLine(String data)
    1392             throws IOException, IllegalStateException {
    1393             if (hasConnection()) {
    1394                 wrappedConnection.printLine(data);
    1395             } else {
    1396                 throw new IllegalStateException("Connection has been released");
    1397             }
    1398         }
    1399
    1400         /**
    1401          * @deprecated
    1402          */
    1403         public String readLine() throws IOException, IllegalStateException {
    1404             if (hasConnection()) {
    1405                 return wrappedConnection.readLine();
    1406             } else {
    1407                 throw new IllegalStateException("Connection has been released");
    1408             }
    1409         }
    1410
    1411         public String readLine(String charset) throws IOException, IllegalStateException {
    1412             if (hasConnection()) {
    1413                 return wrappedConnection.readLine(charset);
    1414             } else {
    1415                 throw new IllegalStateException("Connection has been released");
    1416             }
    1417         }
    1418
    1419         public void releaseConnection() {
    1420             if (!isLocked() && hasConnection()) {
    1421                 HttpConnection wrappedConnection = this.wrappedConnection;
    1422                 this.wrappedConnection = null;
    1423                 wrappedConnection.releaseConnection();
    1424             } else {
    1425                 // do nothing
    1426             }
    1427         }
    1428
    1429         /**
    1430          * @deprecated
    1431          */
    1432         public void setConnectionTimeout(int timeout) {
    1433             if (hasConnection()) {
    1434                 wrappedConnection.setConnectionTimeout(timeout);
    1435             } else {
    1436                 // do nothing
    1437             }
    1438         }
    1439
    1440         public void setHost(String host) throws IllegalStateException {
    1441             if (hasConnection()) {
    1442                 wrappedConnection.setHost(host);
    1443             } else {
    1444                 // do nothing
    1445             }
    1446         }
    1447
    1448         public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
    1449             if (hasConnection()) {
    1450                 wrappedConnection.setHttpConnectionManager(httpConnectionManager);
    1451             } else {
    1452                 // do nothing
    1453             }
    1454         }
    1455
    1456         public void setLastResponseInputStream(InputStream inStream) {
    1457             if (hasConnection()) {
    1458                 wrappedConnection.setLastResponseInputStream(inStream);
    1459             } else {
    1460                 // do nothing
    1461             }
    1462         }
    1463
    1464         public void setPort(int port) throws IllegalStateException {
    1465             if (hasConnection()) {
    1466                 wrappedConnection.setPort(port);
    1467             } else {
    1468                 // do nothing
    1469             }
    1470         }
    1471
    1472         public void setProtocol(Protocol protocol) {
    1473             if (hasConnection()) {
    1474                 wrappedConnection.setProtocol(protocol);
    1475             } else {
    1476                 // do nothing
    1477             }
    1478         }
    1479
    1480         public void setProxyHost(String host) throws IllegalStateException {
    1481             if (hasConnection()) {
    1482                 wrappedConnection.setProxyHost(host);
    1483             } else {
    1484                 // do nothing
    1485             }
    1486         }
    1487
    1488         public void setProxyPort(int port) throws IllegalStateException {
    1489             if (hasConnection()) {
    1490                 wrappedConnection.setProxyPort(port);
    1491             } else {
    1492                 // do nothing
    1493             }
    1494         }
    1495
    1496         /**
    1497          * @deprecated
    1498          */
    1499         public void setSoTimeout(int timeout)
    1500             throws SocketException, IllegalStateException {
    1501             if (hasConnection()) {
    1502                 wrappedConnection.setSoTimeout(timeout);
    1503             } else {
    1504                 // do nothing
    1505             }
    1506         }
    1507
    1508         /**
    1509          * @deprecated
    1510          */
    1511         public void shutdownOutput() {
    1512             if (hasConnection()) {
    1513                 wrappedConnection.shutdownOutput();
    1514             } else {
    1515                 // do nothing
    1516             }
    1517         }
    1518
    1519         public void tunnelCreated() throws IllegalStateException, IOException {
    1520             if (hasConnection()) {
    1521                 wrappedConnection.tunnelCreated();
    1522             } else {
    1523                 // do nothing
    1524             }
    1525         }
    1526
    1527         public void write(byte[] data, int offset, int length)
    1528             throws IOException, IllegalStateException {
    1529             if (hasConnection()) {
    1530                 wrappedConnection.write(data, offset, length);
    1531             } else {
    1532                 throw new IllegalStateException("Connection has been released");
    1533             }
    1534         }
    1535
    1536         public void write(byte[] data)
    1537             throws IOException, IllegalStateException {
    1538             if (hasConnection()) {
    1539                 wrappedConnection.write(data);
    1540             } else {
    1541                 throw new IllegalStateException("Connection has been released");
    1542             }
    1543         }
    1544
    1545         public void writeLine()
    1546             throws IOException, IllegalStateException {
    1547             if (hasConnection()) {
    1548                 wrappedConnection.writeLine();
    1549             } else {
    1550                 throw new IllegalStateException("Connection has been released");
    1551             }
    1552         }
    1553
    1554         public void writeLine(byte[] data)
    1555             throws IOException, IllegalStateException {
    1556             if (hasConnection()) {
    1557                 wrappedConnection.writeLine(data);
    1558             } else {
    1559                 throw new IllegalStateException("Connection has been released");
    1560             }
    1561         }
    1562
    1563         public void flushRequestOutputStream() throws IOException {
    1564             if (hasConnection()) {
    1565                 wrappedConnection.flushRequestOutputStream();
    1566             } else {
    1567                 throw new IllegalStateException("Connection has been released");
    1568             }
    1569         }
    1570
    1571         /**
    1572          * @deprecated
    1573          */
    1574         public int getSoTimeout() throws SocketException {
    1575             if (hasConnection()) {
    1576                 return wrappedConnection.getSoTimeout();
    1577             } else {
    1578                 throw new IllegalStateException("Connection has been released");
    1579             }
    1580         }
    1581
    1582         /**
    1583          * @deprecated
    1584          */
    1585         public String getVirtualHost() {
    1586             if (hasConnection()) {
    1587                 return wrappedConnection.getVirtualHost();
    1588             } else {
    1589                 throw new IllegalStateException("Connection has been released");
    1590             }
    1591         }
    1592
    1593         /**
    1594          * @deprecated
    1595          */
    1596         public void setVirtualHost(String host) throws IllegalStateException {
    1597             if (hasConnection()) {
    1598                 wrappedConnection.setVirtualHost(host);
    1599             } else {
    1600                 throw new IllegalStateException("Connection has been released");
    1601             }
    1602         }
    1603
    1604         public int getSendBufferSize() throws SocketException {
    1605             if (hasConnection()) {
    1606                 return wrappedConnection.getSendBufferSize();
    1607             } else {
    1608                 throw new IllegalStateException("Connection has been released");
    1609             }
    1610         }
    1611
    1612         /**
    1613          * @deprecated
    1614          */
    1615         public void setSendBufferSize(int sendBufferSize) throws SocketException {
    1616             if (hasConnection()) {
    1617                 wrappedConnection.setSendBufferSize(sendBufferSize);
    1618             } else {
    1619                 throw new IllegalStateException("Connection has been released");
    1620             }
    1621         }
    1622
    1623         public HttpConnectionParams getParams() {
    1624             if (hasConnection()) {
    1625                 return wrappedConnection.getParams();
    1626             } else {
    1627                 throw new IllegalStateException("Connection has been released");
    1628             }
    1629         }
    1630
    1631         public void setParams(final HttpConnectionParams params) {
    1632             if (hasConnection()) {
    1633                 wrappedConnection.setParams(params);
    1634             } else {
    1635                 throw new IllegalStateException("Connection has been released");
    1636             }
    1637         }
    1638
    1639         /* (non-Javadoc)
    1640          * @see org.apache.commons.httpclient.HttpConnection#print(java.lang.String, java.lang.String)
    1641          */
    1642         public void print(String data, String charset) throws IOException, IllegalStateException {
    1643             if (hasConnection()) {
    1644                 wrappedConnection.print(data, charset);
    1645             } else {
    1646                 throw new IllegalStateException("Connection has been released");
    1647             }
    1648         }
    1649
    1650         /* (non-Javadoc)
    1651          * @see org.apache.commons.httpclient.HttpConnection#printLine(java.lang.String, java.lang.String)
    1652          */
    1653         public void printLine(String data, String charset)
    1654             throws IOException, IllegalStateException {
    1655             if (hasConnection()) {
    1656                 wrappedConnection.printLine(data, charset);
    1657             } else {
    1658                 throw new IllegalStateException("Connection has been released");
    1659             }
    1660         }
    1661
    1662         /* (non-Javadoc)
    1663          * @see org.apache.commons.httpclient.HttpConnection#setSocketTimeout(int)
    1664          */
    1665         public void setSocketTimeout(int timeout) throws SocketException, IllegalStateException {
    1666             if (hasConnection()) {
    1667                 wrappedConnection.setSocketTimeout(timeout);
    1668             } else {
    1669                 throw new IllegalStateException("Connection has been released");
    1670             }
    1671         }
    1672
    1673     }
    1674
    1675 }
    1676