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: 366716 $
4    * $Date: 2006-01-07 08:04:58 -0500 (Sat, 07 Jan 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      * @see HttpConnectionManager#getConnectionWithTimeout(HostConfiguration, long)
377      * 
378      * @since 3.0
379      */
380     public HttpConnection getConnectionWithTimeout(HostConfiguration hostConfiguration, 
381         long timeout) throws ConnectionPoolTimeoutException {
382 
383         LOG.trace("enter HttpConnectionManager.getConnectionWithTimeout(HostConfiguration, long)");
384 
385         if (hostConfiguration == null) {
386             throw new IllegalArgumentException("hostConfiguration is null");
387         }
388 
389         if (LOG.isDebugEnabled()) {
390             LOG.debug("HttpConnectionManager.getConnection:  config = "
391                 + hostConfiguration + ", timeout = " + timeout);
392         }
393 
394         final HttpConnection conn = doGetConnection(hostConfiguration, timeout);
395 
396         // wrap the connection in an adapter so we can ensure it is used 
397         // only once
398         return new HttpConnectionAdapter(conn);
399     }
400 
401 	/***
402 	 * @see HttpConnectionManager#getConnection(HostConfiguration, long)
403 	 * 
404 	 * @deprecated Use #getConnectionWithTimeout(HostConfiguration, long)
405 	 */
406 	public HttpConnection getConnection(HostConfiguration hostConfiguration, 
407 		long timeout) throws HttpException {
408 
409 		LOG.trace("enter HttpConnectionManager.getConnection(HostConfiguration, long)");
410 		try {
411 			return getConnectionWithTimeout(hostConfiguration, timeout);
412 		} catch(ConnectionPoolTimeoutException e) {
413 			throw new HttpException(e.getMessage());
414 		}
415 	}
416 
417     /***
418      * Gets a connection or waits if one is not available.  A connection is
419      * available if one exists that is not being used or if fewer than
420      * maxHostConnections have been created in the connectionPool, and fewer
421      * than maxTotalConnections have been created in all connectionPools.
422      *
423      * @param hostConfiguration The host configuration.
424      * @param timeout the number of milliseconds to wait for a connection, 0 to
425      * wait indefinitely
426      *
427      * @return HttpConnection an available connection
428      *
429      * @throws HttpException if a connection does not become available in
430      * 'timeout' milliseconds
431      */
432     private HttpConnection doGetConnection(HostConfiguration hostConfiguration, 
433         long timeout) throws ConnectionPoolTimeoutException {
434 
435         HttpConnection connection = null;
436 
437         int maxHostConnections = this.params.getMaxConnectionsPerHost(hostConfiguration);
438         int maxTotalConnections = this.params.getMaxTotalConnections();
439         
440         synchronized (connectionPool) {
441 
442             // we clone the hostConfiguration
443             // so that it cannot be changed once the connection has been retrieved
444             hostConfiguration = new HostConfiguration(hostConfiguration);
445             HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration);
446             WaitingThread waitingThread = null;
447 
448             boolean useTimeout = (timeout > 0);
449             long timeToWait = timeout;
450             long startWait = 0;
451             long endWait = 0;
452 
453             while (connection == null) {
454 
455                 if (shutdown) {
456                     throw new IllegalStateException("Connection factory has been shutdown.");
457                 }
458                 
459                 // happen to have a free connection with the right specs
460                 //
461                 if (hostPool.freeConnections.size() > 0) {
462                     connection = connectionPool.getFreeConnection(hostConfiguration);
463 
464                 // have room to make more
465                 //
466                 } else if ((hostPool.numConnections < maxHostConnections) 
467                     && (connectionPool.numConnections < maxTotalConnections)) {
468 
469                     connection = connectionPool.createConnection(hostConfiguration);
470 
471                 // have room to add host connection, and there is at least one free
472                 // connection that can be liberated to make overall room
473                 //
474                 } else if ((hostPool.numConnections < maxHostConnections) 
475                     && (connectionPool.freeConnections.size() > 0)) {
476 
477                     connectionPool.deleteLeastUsedConnection();
478                     connection = connectionPool.createConnection(hostConfiguration);
479 
480                 // otherwise, we have to wait for one of the above conditions to
481                 // become true
482                 //
483                 } else {
484                     // TODO: keep track of which hostConfigurations have waiting
485                     // threads, so they avoid being sacrificed before necessary
486 
487                     try {
488                         
489                         if (useTimeout && timeToWait <= 0) {
490                             throw new ConnectionPoolTimeoutException("Timeout waiting for connection");
491                         }
492                         
493                         if (LOG.isDebugEnabled()) {
494                             LOG.debug("Unable to get a connection, waiting..., hostConfig=" + hostConfiguration);
495                         }
496                         
497                         if (waitingThread == null) {
498                             waitingThread = new WaitingThread();
499                             waitingThread.hostConnectionPool = hostPool;
500                             waitingThread.thread = Thread.currentThread();
501                         }
502                                     
503                         if (useTimeout) {
504                             startWait = System.currentTimeMillis();
505                         }
506                         
507                         hostPool.waitingThreads.addLast(waitingThread);
508                         connectionPool.waitingThreads.addLast(waitingThread);
509                         connectionPool.wait(timeToWait);
510                         
511                         // we have not been interrupted so we need to remove ourselves from the 
512                         // wait queue
513                         hostPool.waitingThreads.remove(waitingThread);
514                         connectionPool.waitingThreads.remove(waitingThread);
515                     } catch (InterruptedException e) {
516                         // do nothing
517                     } finally {
518                         if (useTimeout) {
519                             endWait = System.currentTimeMillis();
520                             timeToWait -= (endWait - startWait);
521                         }
522                     }
523                 }
524             }
525         }
526         return connection;
527     }
528 
529     /***
530      * Gets the total number of pooled connections for the given host configuration.  This 
531      * is the total number of connections that have been created and are still in use 
532      * by this connection manager for the host configuration.  This value will
533      * not exceed the {@link #getMaxConnectionsPerHost() maximum number of connections per
534      * host}.
535      * 
536      * @param hostConfiguration The host configuration
537      * @return The total number of pooled connections
538      */
539     public int getConnectionsInPool(HostConfiguration hostConfiguration) {
540         synchronized (connectionPool) {
541             HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration);
542             return hostPool.numConnections;
543         }
544     }
545 
546     /***
547      * Gets the total number of pooled connections.  This is the total number of 
548      * connections that have been created and are still in use by this connection 
549      * manager.  This value will not exceed the {@link #getMaxTotalConnections() 
550      * maximum number of connections}.
551      * 
552      * @return the total number of pooled connections
553      */
554     public int getConnectionsInPool() {
555         synchronized (connectionPool) {
556             return connectionPool.numConnections;
557         }
558     }
559     
560     /***
561      * Gets the number of connections in use for this configuration.
562      *
563      * @param hostConfiguration the key that connections are tracked on
564      * @return the number of connections in use
565      * 
566      * @deprecated Use {@link #getConnectionsInPool(HostConfiguration)}
567      */
568     public int getConnectionsInUse(HostConfiguration hostConfiguration) {
569         return getConnectionsInPool(hostConfiguration);
570     }
571 
572     /***
573      * Gets the total number of connections in use.
574      * 
575      * @return the total number of connections in use
576      * 
577      * @deprecated Use {@link #getConnectionsInPool()}
578      */
579     public int getConnectionsInUse() {
580         return getConnectionsInPool();
581     }
582 
583     /***
584      * Deletes all closed connections.  Only connections currently owned by the connection
585      * manager are processed.
586      * 
587      * @see HttpConnection#isOpen()
588      * 
589      * @since 3.0
590      */
591     public void deleteClosedConnections() {
592         connectionPool.deleteClosedConnections();
593     }
594     
595     /***
596      * @since 3.0
597      */
598     public void closeIdleConnections(long idleTimeout) {
599         connectionPool.closeIdleConnections(idleTimeout);
600     }
601     
602     /***
603      * Make the given HttpConnection available for use by other requests.
604      * If another thread is blocked in getConnection() that could use this
605      * connection, it will be woken up.
606      *
607      * @param conn the HttpConnection to make available.
608      */
609     public void releaseConnection(HttpConnection conn) {
610         LOG.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
611 
612         if (conn instanceof HttpConnectionAdapter) {
613             // connections given out are wrapped in an HttpConnectionAdapter
614             conn = ((HttpConnectionAdapter) conn).getWrappedConnection();
615         } else {
616             // this is okay, when an HttpConnectionAdapter is released
617             // is releases the real connection
618         }
619 
620         // make sure that the response has been read.
621         SimpleHttpConnectionManager.finishLastResponse(conn);
622 
623         connectionPool.freeConnection(conn);
624     }
625 
626     /***
627      * Gets the host configuration for a connection.
628      * @param conn the connection to get the configuration of
629      * @return a new HostConfiguration
630      */
631     private HostConfiguration configurationForConnection(HttpConnection conn) {
632 
633         HostConfiguration connectionConfiguration = new HostConfiguration();
634         
635         connectionConfiguration.setHost(
636             conn.getHost(), 
637             conn.getPort(), 
638             conn.getProtocol()
639         );
640         if (conn.getLocalAddress() != null) {
641             connectionConfiguration.setLocalAddress(conn.getLocalAddress());
642         }
643         if (conn.getProxyHost() != null) {
644             connectionConfiguration.setProxy(conn.getProxyHost(), conn.getProxyPort());
645         }
646 
647         return connectionConfiguration;
648     }
649 
650     /***
651      * Returns {@link HttpConnectionManagerParams parameters} associated 
652      * with this connection manager.
653      * 
654      * @since 3.0
655      * 
656      * @see HttpConnectionManagerParams
657      */
658     public HttpConnectionManagerParams getParams() {
659         return this.params;
660     }
661 
662     /***
663      * Assigns {@link HttpConnectionManagerParams parameters} for this 
664      * connection manager.
665      * 
666      * @since 3.0
667      * 
668      * @see HttpConnectionManagerParams
669      */
670     public void setParams(final HttpConnectionManagerParams params) {
671         if (params == null) {
672             throw new IllegalArgumentException("Parameters may not be null");
673         }
674         this.params = params;
675     }
676     
677     /***
678      * Global Connection Pool, including per-host pools
679      */
680     private class ConnectionPool {
681         
682         /*** The list of free connections */
683         private LinkedList freeConnections = new LinkedList();
684 
685         /*** The list of WaitingThreads waiting for a connection */
686         private LinkedList waitingThreads = new LinkedList();
687 
688         /***
689          * Map where keys are {@link HostConfiguration}s and values are {@link
690          * HostConnectionPool}s
691          */
692         private final Map mapHosts = new HashMap();
693 
694         private IdleConnectionHandler idleConnectionHandler = new IdleConnectionHandler();        
695         
696         /*** The number of created connections */
697         private int numConnections = 0;
698 
699         /***
700          * Cleans up all connection pool resources.
701          */
702         public synchronized void shutdown() {
703             
704             // close all free connections
705             Iterator iter = freeConnections.iterator();
706             while (iter.hasNext()) {
707                 HttpConnection conn = (HttpConnection) iter.next();
708                 iter.remove();
709                 conn.close();
710             }
711             
712             // close all connections that have been checked out
713             shutdownCheckedOutConnections(this);
714             
715             // interrupt all waiting threads
716             iter = waitingThreads.iterator();
717             while (iter.hasNext()) {
718                 WaitingThread waiter = (WaitingThread) iter.next();
719                 iter.remove();
720                 waiter.thread.interrupt();
721             }
722             
723             // clear out map hosts
724             mapHosts.clear();
725             
726             // remove all references to connections
727             idleConnectionHandler.removeAll();
728         }
729         
730         /***
731          * Creates a new connection and returns it for use of the calling method.
732          *
733          * @param hostConfiguration the configuration for the connection
734          * @return a new connection or <code>null</code> if none are available
735          */
736         public synchronized HttpConnection createConnection(HostConfiguration hostConfiguration) {
737             HostConnectionPool hostPool = getHostPool(hostConfiguration);
738             if (LOG.isDebugEnabled()) {
739                 LOG.debug("Allocating new connection, hostConfig=" + hostConfiguration);
740             }
741             HttpConnectionWithReference connection = new HttpConnectionWithReference(
742                     hostConfiguration);
743             connection.getParams().setDefaults(MultiThreadedHttpConnectionManager.this.params);
744             connection.setHttpConnectionManager(MultiThreadedHttpConnectionManager.this);
745             numConnections++;
746             hostPool.numConnections++;
747     
748             // store a reference to this connection so that it can be cleaned up
749             // in the event it is not correctly released
750             storeReferenceToConnection(connection, hostConfiguration, this);
751             return connection;
752         }
753     
754         /***
755          * Handles cleaning up for a lost connection with the given config.  Decrements any 
756          * connection counts and notifies waiting threads, if appropriate.
757          * 
758          * @param config the host configuration of the connection that was lost
759          */
760         public synchronized void handleLostConnection(HostConfiguration config) {
761             HostConnectionPool hostPool = getHostPool(config);
762             hostPool.numConnections--;
763 
764             numConnections--;
765             notifyWaitingThread(config);
766         }
767 
768         /***
769          * Get the pool (list) of connections available for the given hostConfig.
770          *
771          * @param hostConfiguration the configuraton for the connection pool
772          * @return a pool (list) of connections available for the given config
773          */
774         public synchronized HostConnectionPool getHostPool(HostConfiguration hostConfiguration) {
775             LOG.trace("enter HttpConnectionManager.ConnectionPool.getHostPool(HostConfiguration)");
776 
777             // Look for a list of connections for the given config
778             HostConnectionPool listConnections = (HostConnectionPool) 
779                 mapHosts.get(hostConfiguration);
780             if (listConnections == null) {
781                 // First time for this config
782                 listConnections = new HostConnectionPool();
783                 listConnections.hostConfiguration = hostConfiguration;
784                 mapHosts.put(hostConfiguration, listConnections);
785             }
786             
787             return listConnections;
788         }
789 
790         /***
791          * If available, get a free connection for this host
792          *
793          * @param hostConfiguration the configuraton for the connection pool
794          * @return an available connection for the given config
795          */
796         public synchronized HttpConnection getFreeConnection(HostConfiguration hostConfiguration) {
797 
798             HttpConnectionWithReference connection = null;
799             
800             HostConnectionPool hostPool = getHostPool(hostConfiguration);
801 
802             if (hostPool.freeConnections.size() > 0) {
803                 connection = (HttpConnectionWithReference) hostPool.freeConnections.removeFirst();
804                 freeConnections.remove(connection);
805                 // store a reference to this connection so that it can be cleaned up
806                 // in the event it is not correctly released
807                 storeReferenceToConnection(connection, hostConfiguration, this);
808                 if (LOG.isDebugEnabled()) {
809                     LOG.debug("Getting free connection, hostConfig=" + hostConfiguration);
810                 }
811 
812                 // remove the connection from the timeout handler
813                 idleConnectionHandler.remove(connection);
814             } else if (LOG.isDebugEnabled()) {
815                 LOG.debug("There were no free connections to get, hostConfig=" 
816                     + hostConfiguration);
817             }
818             return connection;
819         }
820         
821         /***
822          * Deletes all closed connections.
823          */        
824         public synchronized void deleteClosedConnections() {
825             
826             Iterator iter = freeConnections.iterator();
827             
828             while (iter.hasNext()) {
829                 HttpConnection conn = (HttpConnection) iter.next();
830                 if (!conn.isOpen()) {
831                     iter.remove();
832                     deleteConnection(conn);
833                 }
834             }
835         }
836 
837         /***
838          * Closes idle connections.
839          * @param idleTimeout
840          */
841         public synchronized void closeIdleConnections(long idleTimeout) {
842             idleConnectionHandler.closeIdleConnections(idleTimeout);
843         }
844         
845         /***
846          * Deletes the given connection.  This will remove all reference to the connection
847          * so that it can be GCed.
848          * 
849          * <p><b>Note:</b> Does not remove the connection from the freeConnections list.  It
850          * is assumed that the caller has already handled this case.</p>
851          * 
852          * @param connection The connection to delete
853          */
854         private synchronized void deleteConnection(HttpConnection connection) {
855             
856             HostConfiguration connectionConfiguration = configurationForConnection(connection);
857 
858             if (LOG.isDebugEnabled()) {
859                 LOG.debug("Reclaiming connection, hostConfig=" + connectionConfiguration);
860             }
861 
862             connection.close();
863 
864             HostConnectionPool hostPool = getHostPool(connectionConfiguration);
865             
866             hostPool.freeConnections.remove(connection);
867             hostPool.numConnections--;
868             numConnections--;
869 
870             // remove the connection from the timeout handler
871             idleConnectionHandler.remove(connection);            
872         }
873         
874         /***
875          * Close and delete an old, unused connection to make room for a new one.
876          */
877         public synchronized void deleteLeastUsedConnection() {
878 
879             HttpConnection connection = (HttpConnection) freeConnections.removeFirst();
880 
881             if (connection != null) {
882                 deleteConnection(connection);
883             } else if (LOG.isDebugEnabled()) {
884                 LOG.debug("Attempted to reclaim an unused connection but there were none.");
885             }
886         }
887 
888         /***
889          * Notifies a waiting thread that a connection for the given configuration is 
890          * available.
891          * @param configuration the host config to use for notifying
892          * @see #notifyWaitingThread(HostConnectionPool)
893          */
894         public synchronized void notifyWaitingThread(HostConfiguration configuration) {
895             notifyWaitingThread(getHostPool(configuration));
896         }
897 
898         /***
899          * Notifies a waiting thread that a connection for the given configuration is 
900          * available.  This will wake a thread waiting in this host pool or if there is not
901          * one a thread in the connection pool will be notified.
902          * 
903          * @param hostPool the host pool to use for notifying
904          */
905         public synchronized void notifyWaitingThread(HostConnectionPool hostPool) {
906 
907             // find the thread we are going to notify, we want to ensure that each
908             // waiting thread is only interrupted once so we will remove it from 
909             // all wait queues before interrupting it
910             WaitingThread waitingThread = null;
911                 
912             if (hostPool.waitingThreads.size() > 0) {
913                 if (LOG.isDebugEnabled()) {
914                     LOG.debug("Notifying thread waiting on host pool, hostConfig=" 
915                         + hostPool.hostConfiguration);
916                 }                
917                 waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
918                 waitingThreads.remove(waitingThread);
919             } else if (waitingThreads.size() > 0) {
920                 if (LOG.isDebugEnabled()) {
921                     LOG.debug("No-one waiting on host pool, notifying next waiting thread.");
922                 }
923                 waitingThread = (WaitingThread) waitingThreads.removeFirst();
924                 waitingThread.hostConnectionPool.waitingThreads.remove(waitingThread);
925             } else if (LOG.isDebugEnabled()) {
926                 LOG.debug("Notifying no-one, there are no waiting threads");
927             }
928                 
929             if (waitingThread != null) {
930                 waitingThread.thread.interrupt();
931             }
932         }
933 
934         /***
935          * Marks the given connection as free.
936          * @param conn a connection that is no longer being used
937          */
938         public void freeConnection(HttpConnection conn) {
939 
940             HostConfiguration connectionConfiguration = configurationForConnection(conn);
941 
942             if (LOG.isDebugEnabled()) {
943                 LOG.debug("Freeing connection, hostConfig=" + connectionConfiguration);
944             }
945 
946             synchronized (this) {
947                 
948                 if (shutdown) {
949                     // the connection manager has been shutdown, release the connection's
950                     // resources and get out of here
951                     conn.close();
952                     return;
953                 }
954                 
955                 HostConnectionPool hostPool = getHostPool(connectionConfiguration);
956 
957                 // Put the connect back in the available list and notify a waiter
958                 hostPool.freeConnections.add(conn);
959                 if (hostPool.numConnections == 0) {
960                     // for some reason this connection pool didn't already exist
961                     LOG.error("Host connection pool not found, hostConfig=" 
962                               + connectionConfiguration);
963                     hostPool.numConnections = 1;
964                 }
965 
966                 freeConnections.add(conn);
967                 // we can remove the reference to this connection as we have control over
968                 // it again.  this also ensures that the connection manager can be GCed
969                 removeReferenceToConnection((HttpConnectionWithReference) conn);
970                 if (numConnections == 0) {
971                     // for some reason this connection pool didn't already exist
972                     LOG.error("Host connection pool not found, hostConfig=" 
973                               + connectionConfiguration);
974                     numConnections = 1;
975                 }
976 
977                 // register the connection with the timeout handler
978                 idleConnectionHandler.add(conn);
979 
980                 notifyWaitingThread(hostPool);
981             }
982         }
983     }
984 
985     /***
986      * A simple struct-like class to combine the objects needed to release a connection's
987      * resources when claimed by the garbage collector.
988      */
989     private static class ConnectionSource {
990         
991         /*** The connection pool that created the connection */
992         public ConnectionPool connectionPool;
993 
994         /*** The connection's host configuration */
995         public HostConfiguration hostConfiguration;
996     }
997     
998     /***
999      * A simple struct-like class to combine the connection list and the count
1000      * of created connections.
1001      */
1002     private static class HostConnectionPool {
1003         /*** The hostConfig this pool is for */
1004         public HostConfiguration hostConfiguration;
1005         
1006         /*** The list of free connections */
1007         public LinkedList freeConnections = new LinkedList();
1008         
1009         /*** The list of WaitingThreads for this host */
1010         public LinkedList waitingThreads = new LinkedList();
1011 
1012         /*** The number of created connections */
1013         public int numConnections = 0;
1014     }
1015     
1016     /***
1017      * A simple struct-like class to combine the waiting thread and the connection 
1018      * pool it is waiting on.
1019      */
1020     private static class WaitingThread {
1021         /*** The thread that is waiting for a connection */
1022         public Thread thread;
1023         
1024         /*** The connection pool the thread is waiting for */
1025         public HostConnectionPool hostConnectionPool;
1026     }
1027 
1028     /***
1029      * A thread for listening for HttpConnections reclaimed by the garbage
1030      * collector.
1031      */
1032     private static class ReferenceQueueThread extends Thread {
1033 
1034         private boolean shutdown = false;
1035         
1036         /***
1037          * Create an instance and make this a daemon thread.
1038          */
1039         public ReferenceQueueThread() {
1040             setDaemon(true);
1041             setName("MultiThreadedHttpConnectionManager cleanup");
1042         }
1043 
1044         public void shutdown() {
1045             this.shutdown = true;
1046         }
1047         
1048         /***
1049          * Handles cleaning up for the given connection reference.
1050          * 
1051          * @param ref the reference to clean up
1052          */
1053         private void handleReference(Reference ref) {
1054             
1055             ConnectionSource source = null;
1056             
1057             synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
1058                 source = (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.remove(ref);
1059             }
1060             // only clean up for this reference if it is still associated with 
1061             // a ConnectionSource
1062             if (source != null) {
1063                 if (LOG.isDebugEnabled()) {
1064                     LOG.debug(
1065                         "Connection reclaimed by garbage collector, hostConfig=" 
1066                         + source.hostConfiguration);
1067                 }
1068                 
1069                 source.connectionPool.handleLostConnection(source.hostConfiguration);
1070             }
1071         }
1072 
1073         /***
1074          * Start execution.
1075          */
1076         public void run() {
1077             while (!shutdown) {
1078                 try {
1079                     // remove the next reference and process it, a timeout 
1080                     // is used so that the thread does not block indefinitely 
1081                     // and therefore keep the thread from shutting down
1082                     Reference ref = REFERENCE_QUEUE.remove(1000);
1083                     if (ref != null) {
1084                         handleReference(ref);
1085                     }
1086                 } catch (InterruptedException e) {
1087                     LOG.debug("ReferenceQueueThread interrupted", e);
1088                 }
1089             }
1090         }
1091 
1092     }
1093     
1094     /***
1095      * A connection that keeps a reference to itself.
1096      */
1097     private static class HttpConnectionWithReference extends HttpConnection {
1098         
1099         public WeakReference reference = new WeakReference(this, REFERENCE_QUEUE);
1100         
1101         /***
1102          * @param hostConfiguration
1103          */
1104         public HttpConnectionWithReference(HostConfiguration hostConfiguration) {
1105             super(hostConfiguration);
1106         }
1107 
1108     }
1109     
1110     /***
1111      * An HttpConnection wrapper that ensures a connection cannot be used
1112      * once released.
1113      */
1114     private static class HttpConnectionAdapter extends HttpConnection {
1115 
1116         // the wrapped connection
1117         private HttpConnection wrappedConnection;
1118 
1119         /***
1120          * Creates a new HttpConnectionAdapter.
1121          * @param connection the connection to be wrapped
1122          */
1123         public HttpConnectionAdapter(HttpConnection connection) {
1124             super(connection.getHost(), connection.getPort(), connection.getProtocol());
1125             this.wrappedConnection = connection;
1126         }
1127 
1128         /***
1129          * Tests if the wrapped connection is still available.
1130          * @return boolean
1131          */
1132         protected boolean hasConnection() {
1133             return wrappedConnection != null;
1134         }
1135 
1136         /***
1137          * @return HttpConnection
1138          */
1139         HttpConnection getWrappedConnection() {
1140             return wrappedConnection;
1141         }
1142         
1143         public void close() {
1144             if (hasConnection()) {
1145                 wrappedConnection.close();
1146             } else {
1147                 // do nothing
1148             }
1149         }
1150 
1151         public InetAddress getLocalAddress() {
1152             if (hasConnection()) {
1153                 return wrappedConnection.getLocalAddress();
1154             } else {
1155                 return null;
1156             }
1157         }
1158 
1159         /***
1160          * @deprecated
1161          */
1162         public boolean isStaleCheckingEnabled() {
1163             if (hasConnection()) {
1164                 return wrappedConnection.isStaleCheckingEnabled();
1165             } else {
1166                 return false;
1167             }
1168         }
1169 
1170         public void setLocalAddress(InetAddress localAddress) {
1171             if (hasConnection()) {
1172                 wrappedConnection.setLocalAddress(localAddress);
1173             } else {
1174                 throw new IllegalStateException("Connection has been released");
1175             }
1176         }
1177     
1178         /***
1179          * @deprecated 
1180          */
1181         public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
1182             if (hasConnection()) {
1183                 wrappedConnection.setStaleCheckingEnabled(staleCheckEnabled);
1184             } else {
1185                 throw new IllegalStateException("Connection has been released");
1186             }
1187         }
1188 
1189         public String getHost() {
1190             if (hasConnection()) {
1191                 return wrappedConnection.getHost();
1192             } else {
1193                 return null;
1194             }
1195         }
1196 
1197         public HttpConnectionManager getHttpConnectionManager() {
1198             if (hasConnection()) {
1199                 return wrappedConnection.getHttpConnectionManager();
1200             } else {
1201                 return null;
1202             }
1203         }
1204 
1205         public InputStream getLastResponseInputStream() {
1206             if (hasConnection()) {
1207                 return wrappedConnection.getLastResponseInputStream();
1208             } else {
1209                 return null;
1210             }
1211         }
1212 
1213         public int getPort() {
1214             if (hasConnection()) {
1215                 return wrappedConnection.getPort();
1216             } else {
1217                 return -1;
1218             }
1219         }
1220 
1221         public Protocol getProtocol() {
1222             if (hasConnection()) {
1223                 return wrappedConnection.getProtocol();
1224             } else {
1225                 return null;
1226             }
1227         }
1228 
1229         public String getProxyHost() {
1230             if (hasConnection()) {
1231                 return wrappedConnection.getProxyHost();
1232             } else {
1233                 return null;
1234             }
1235         }
1236 
1237         public int getProxyPort() {
1238             if (hasConnection()) {
1239                 return wrappedConnection.getProxyPort();
1240             } else {
1241                 return -1;
1242             }
1243         }
1244 
1245         public OutputStream getRequestOutputStream()
1246             throws IOException, IllegalStateException {
1247             if (hasConnection()) {
1248                 return wrappedConnection.getRequestOutputStream();
1249             } else {
1250                 return null;
1251             }
1252         }
1253 
1254         public InputStream getResponseInputStream()
1255             throws IOException, IllegalStateException {
1256             if (hasConnection()) {
1257                 return wrappedConnection.getResponseInputStream();
1258             } else {
1259                 return null;
1260             }
1261         }
1262 
1263         public boolean isOpen() {
1264             if (hasConnection()) {
1265                 return wrappedConnection.isOpen();
1266             } else {
1267                 return false;
1268             }
1269         }
1270 
1271         public boolean closeIfStale() throws IOException {
1272             if (hasConnection()) {
1273                 return wrappedConnection.closeIfStale();
1274             } else {
1275                 return false;
1276             }
1277         }
1278 
1279         public boolean isProxied() {
1280             if (hasConnection()) {
1281                 return wrappedConnection.isProxied();
1282             } else {
1283                 return false;
1284             }
1285         }
1286 
1287         public boolean isResponseAvailable() throws IOException {
1288             if (hasConnection()) {
1289                 return  wrappedConnection.isResponseAvailable();
1290             } else {
1291                 return false;
1292             }
1293         }
1294 
1295         public boolean isResponseAvailable(int timeout) throws IOException {
1296             if (hasConnection()) {
1297                 return  wrappedConnection.isResponseAvailable(timeout);
1298             } else {
1299                 return false;
1300             }
1301         }
1302 
1303         public boolean isSecure() {
1304             if (hasConnection()) {
1305                 return wrappedConnection.isSecure();
1306             } else {
1307                 return false;
1308             }
1309         }
1310 
1311         public boolean isTransparent() {
1312             if (hasConnection()) {
1313                 return wrappedConnection.isTransparent();
1314             } else {
1315                 return false;
1316             }
1317         }
1318 
1319         public void open() throws IOException {
1320             if (hasConnection()) {
1321                 wrappedConnection.open();
1322             } else {
1323                 throw new IllegalStateException("Connection has been released");
1324             }
1325         }
1326 
1327         /***
1328          * @deprecated
1329          */
1330         public void print(String data)
1331             throws IOException, IllegalStateException {
1332             if (hasConnection()) {
1333                 wrappedConnection.print(data);
1334             } else {
1335                 throw new IllegalStateException("Connection has been released");
1336             }
1337         }
1338 
1339         public void printLine()
1340             throws IOException, IllegalStateException {
1341             if (hasConnection()) {
1342                 wrappedConnection.printLine();
1343             } else {
1344                 throw new IllegalStateException("Connection has been released");
1345             }
1346         }
1347 
1348         /***
1349          * @deprecated
1350          */
1351         public void printLine(String data)
1352             throws IOException, IllegalStateException {
1353             if (hasConnection()) {
1354                 wrappedConnection.printLine(data);
1355             } else {
1356                 throw new IllegalStateException("Connection has been released");
1357             }
1358         }
1359 
1360         /***
1361          * @deprecated
1362          */
1363         public String readLine() throws IOException, IllegalStateException {
1364             if (hasConnection()) {
1365                 return wrappedConnection.readLine();
1366             } else {
1367                 throw new IllegalStateException("Connection has been released");
1368             }
1369         }
1370 
1371         public String readLine(String charset) throws IOException, IllegalStateException {
1372             if (hasConnection()) {
1373                 return wrappedConnection.readLine(charset);
1374             } else {
1375                 throw new IllegalStateException("Connection has been released");
1376             }
1377         }
1378 
1379         public void releaseConnection() {
1380             if (!isLocked() && hasConnection()) {
1381                 HttpConnection wrappedConnection = this.wrappedConnection;
1382                 this.wrappedConnection = null;
1383                 wrappedConnection.releaseConnection();
1384             } else {
1385                 // do nothing
1386             }
1387         }
1388 
1389         /***
1390          * @deprecated
1391          */
1392         public void setConnectionTimeout(int timeout) {
1393             if (hasConnection()) {
1394                 wrappedConnection.setConnectionTimeout(timeout);
1395             } else {
1396                 // do nothing
1397             }
1398         }
1399 
1400         public void setHost(String host) throws IllegalStateException {
1401             if (hasConnection()) {
1402                 wrappedConnection.setHost(host);
1403             } else {
1404                 // do nothing
1405             }
1406         }
1407 
1408         public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1409             if (hasConnection()) {
1410                 wrappedConnection.setHttpConnectionManager(httpConnectionManager);
1411             } else {
1412                 // do nothing
1413             }
1414         }
1415 
1416         public void setLastResponseInputStream(InputStream inStream) {
1417             if (hasConnection()) {
1418                 wrappedConnection.setLastResponseInputStream(inStream);
1419             } else {
1420                 // do nothing
1421             }
1422         }
1423 
1424         public void setPort(int port) throws IllegalStateException {
1425             if (hasConnection()) {
1426                 wrappedConnection.setPort(port);
1427             } else {
1428                 // do nothing
1429             }
1430         }
1431 
1432         public void setProtocol(Protocol protocol) {
1433             if (hasConnection()) {
1434                 wrappedConnection.setProtocol(protocol);
1435             } else {
1436                 // do nothing
1437             }
1438         }
1439 
1440         public void setProxyHost(String host) throws IllegalStateException {
1441             if (hasConnection()) {
1442                 wrappedConnection.setProxyHost(host);
1443             } else {
1444                 // do nothing
1445             }
1446         }
1447 
1448         public void setProxyPort(int port) throws IllegalStateException {
1449             if (hasConnection()) {
1450                 wrappedConnection.setProxyPort(port);
1451             } else {
1452                 // do nothing
1453             }
1454         }
1455 
1456         /***
1457          * @deprecated
1458          */
1459         public void setSoTimeout(int timeout)
1460             throws SocketException, IllegalStateException {
1461             if (hasConnection()) {
1462                 wrappedConnection.setSoTimeout(timeout);
1463             } else {
1464                 // do nothing
1465             }
1466         }
1467 
1468         /***
1469          * @deprecated
1470          */
1471         public void shutdownOutput() {
1472             if (hasConnection()) {
1473                 wrappedConnection.shutdownOutput();
1474             } else {
1475                 // do nothing
1476             }
1477         }
1478 
1479         public void tunnelCreated() throws IllegalStateException, IOException {
1480             if (hasConnection()) {
1481                 wrappedConnection.tunnelCreated();
1482             } else {
1483                 // do nothing
1484             }
1485         }
1486 
1487         public void write(byte[] data, int offset, int length)
1488             throws IOException, IllegalStateException {
1489             if (hasConnection()) {
1490                 wrappedConnection.write(data, offset, length);
1491             } else {
1492                 throw new IllegalStateException("Connection has been released");
1493             }
1494         }
1495 
1496         public void write(byte[] data)
1497             throws IOException, IllegalStateException {
1498             if (hasConnection()) {
1499                 wrappedConnection.write(data);
1500             } else {
1501                 throw new IllegalStateException("Connection has been released");
1502             }
1503         }
1504 
1505         public void writeLine()
1506             throws IOException, IllegalStateException {
1507             if (hasConnection()) {
1508                 wrappedConnection.writeLine();
1509             } else {
1510                 throw new IllegalStateException("Connection has been released");
1511             }
1512         }
1513 
1514         public void writeLine(byte[] data)
1515             throws IOException, IllegalStateException {
1516             if (hasConnection()) {
1517                 wrappedConnection.writeLine(data);
1518             } else {
1519                 throw new IllegalStateException("Connection has been released");
1520             }
1521         }
1522 
1523         public void flushRequestOutputStream() throws IOException {
1524             if (hasConnection()) {
1525                 wrappedConnection.flushRequestOutputStream();
1526             } else {
1527                 throw new IllegalStateException("Connection has been released");
1528             }
1529         }
1530 
1531         /***
1532          * @deprecated
1533          */
1534         public int getSoTimeout() throws SocketException {
1535             if (hasConnection()) {
1536                 return wrappedConnection.getSoTimeout();
1537             } else {
1538                 throw new IllegalStateException("Connection has been released");
1539             }
1540         }
1541 
1542         /***
1543          * @deprecated
1544          */
1545         public String getVirtualHost() {
1546             if (hasConnection()) {
1547                 return wrappedConnection.getVirtualHost();
1548             } else {
1549                 throw new IllegalStateException("Connection has been released");
1550             }
1551         }
1552 
1553         /***
1554          * @deprecated
1555          */
1556         public void setVirtualHost(String host) throws IllegalStateException {
1557             if (hasConnection()) {
1558                 wrappedConnection.setVirtualHost(host);
1559             } else {
1560                 throw new IllegalStateException("Connection has been released");
1561             }
1562         }
1563 
1564         public int getSendBufferSize() throws SocketException {
1565             if (hasConnection()) {
1566                 return wrappedConnection.getSendBufferSize();
1567             } else {
1568                 throw new IllegalStateException("Connection has been released");
1569             }
1570         }
1571 
1572         /***
1573          * @deprecated
1574          */
1575         public void setSendBufferSize(int sendBufferSize) throws SocketException {
1576             if (hasConnection()) {
1577                 wrappedConnection.setSendBufferSize(sendBufferSize);
1578             } else {
1579                 throw new IllegalStateException("Connection has been released");
1580             }
1581         }
1582 
1583         public HttpConnectionParams getParams() {
1584             if (hasConnection()) {
1585                 return wrappedConnection.getParams();
1586             } else {
1587                 throw new IllegalStateException("Connection has been released");
1588             }
1589         }
1590 
1591         public void setParams(final HttpConnectionParams params) {
1592             if (hasConnection()) {
1593                 wrappedConnection.setParams(params);
1594             } else {
1595                 throw new IllegalStateException("Connection has been released");
1596             }
1597         }
1598 
1599         /* (non-Javadoc)
1600          * @see org.apache.commons.httpclient.HttpConnection#print(java.lang.String, java.lang.String)
1601          */
1602         public void print(String data, String charset) throws IOException, IllegalStateException {
1603             if (hasConnection()) {
1604                 wrappedConnection.print(data, charset);
1605             } else {
1606                 throw new IllegalStateException("Connection has been released");
1607             }
1608         }
1609 
1610         /* (non-Javadoc)
1611          * @see org.apache.commons.httpclient.HttpConnection#printLine(java.lang.String, java.lang.String)
1612          */
1613         public void printLine(String data, String charset)
1614             throws IOException, IllegalStateException {
1615             if (hasConnection()) {
1616                 wrappedConnection.printLine(data, charset);
1617             } else {
1618                 throw new IllegalStateException("Connection has been released");
1619             }
1620         }
1621 
1622         /* (non-Javadoc)
1623          * @see org.apache.commons.httpclient.HttpConnection#setSocketTimeout(int)
1624          */
1625         public void setSocketTimeout(int timeout) throws SocketException, IllegalStateException {
1626             if (hasConnection()) {
1627                 wrappedConnection.setSocketTimeout(timeout);
1628             } else {
1629                 throw new IllegalStateException("Connection has been released");
1630             }
1631         }
1632 
1633     }
1634 
1635 }
1636