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