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: 157244 $
4    * $Date: 2005-03-12 06:30:25 -0500 (Sat, 12 Mar 2005) $
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.getMaxTotalConnections();
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             
738             HttpConnectionWithReference connection = null;
739 
740             HostConnectionPool hostPool = getHostPool(hostConfiguration);
741 
742             if ((hostPool.numConnections < getMaxConnectionsPerHost()) 
743                 && (numConnections < getMaxTotalConnections())) {
744 
745                 if (LOG.isDebugEnabled()) {
746                     LOG.debug("Allocating new connection, hostConfig=" + hostConfiguration);
747                 }
748                 connection = new HttpConnectionWithReference(hostConfiguration);
749                 connection.getParams().setDefaults(MultiThreadedHttpConnectionManager.this.params);
750                 connection.setHttpConnectionManager(MultiThreadedHttpConnectionManager.this);
751                 numConnections++;
752                 hostPool.numConnections++;
753         
754                 // store a reference to this connection so that it can be cleaned up
755                 // in the event it is not correctly released
756                 storeReferenceToConnection(connection, hostConfiguration, this);
757 
758             } else if (LOG.isDebugEnabled()) {
759                 if (hostPool.numConnections >= getMaxConnectionsPerHost()) {
760                     LOG.debug("No connection allocated, host pool has already reached "
761                         + "maxConnectionsPerHost, hostConfig=" + hostConfiguration
762                         + ", maxConnectionsPerhost=" + getMaxConnectionsPerHost());
763                 } else {
764                     LOG.debug("No connection allocated, maxTotalConnections reached, "
765                         + "maxTotalConnections=" + getMaxTotalConnections());
766                 }
767             }
768             
769             return connection;
770         }
771     
772         /***
773          * Handles cleaning up for a lost connection with the given config.  Decrements any 
774          * connection counts and notifies waiting threads, if appropriate.
775          * 
776          * @param config the host configuration of the connection that was lost
777          */
778         public synchronized void handleLostConnection(HostConfiguration config) {
779             HostConnectionPool hostPool = getHostPool(config);
780             hostPool.numConnections--;
781 
782             numConnections--;
783             notifyWaitingThread(config);
784         }
785 
786         /***
787          * Get the pool (list) of connections available for the given hostConfig.
788          *
789          * @param hostConfiguration the configuraton for the connection pool
790          * @return a pool (list) of connections available for the given config
791          */
792         public synchronized HostConnectionPool getHostPool(HostConfiguration hostConfiguration) {
793             LOG.trace("enter HttpConnectionManager.ConnectionPool.getHostPool(HostConfiguration)");
794 
795             // Look for a list of connections for the given config
796             HostConnectionPool listConnections = (HostConnectionPool) 
797                 mapHosts.get(hostConfiguration);
798             if (listConnections == null) {
799                 // First time for this config
800                 listConnections = new HostConnectionPool();
801                 listConnections.hostConfiguration = hostConfiguration;
802                 mapHosts.put(hostConfiguration, listConnections);
803             }
804             
805             return listConnections;
806         }
807 
808         /***
809          * If available, get a free connection for this host
810          *
811          * @param hostConfiguration the configuraton for the connection pool
812          * @return an available connection for the given config
813          */
814         public synchronized HttpConnection getFreeConnection(HostConfiguration hostConfiguration) {
815 
816             HttpConnectionWithReference connection = null;
817             
818             HostConnectionPool hostPool = getHostPool(hostConfiguration);
819 
820             if (hostPool.freeConnections.size() > 0) {
821                 connection = (HttpConnectionWithReference) hostPool.freeConnections.removeFirst();
822                 freeConnections.remove(connection);
823                 // store a reference to this connection so that it can be cleaned up
824                 // in the event it is not correctly released
825                 storeReferenceToConnection(connection, hostConfiguration, this);
826                 if (LOG.isDebugEnabled()) {
827                     LOG.debug("Getting free connection, hostConfig=" + hostConfiguration);
828                 }
829 
830                 // remove the connection from the timeout handler
831                 idleConnectionHandler.remove(connection);
832             } else if (LOG.isDebugEnabled()) {
833                 LOG.debug("There were no free connections to get, hostConfig=" 
834                     + hostConfiguration);
835             }
836             return connection;
837         }
838         
839         /***
840          * Deletes all closed connections.
841          */        
842         public synchronized void deleteClosedConnections() {
843             
844             Iterator iter = freeConnections.iterator();
845             
846             while (iter.hasNext()) {
847                 HttpConnection conn = (HttpConnection) iter.next();
848                 if (!conn.isOpen()) {
849                     iter.remove();
850                     deleteConnection(conn);
851                 }
852             }
853         }
854 
855         /***
856          * Closes idle connections.
857          * @param idleTimeout
858          */
859         public synchronized void closeIdleConnections(long idleTimeout) {
860             idleConnectionHandler.closeIdleConnections(idleTimeout);
861         }
862         
863         /***
864          * Deletes the given connection.  This will remove all reference to the connection
865          * so that it can be GCed.
866          * 
867          * <p><b>Note:</b> Does not remove the connection from the freeConnections list.  It
868          * is assumed that the caller has already handled this case.</p>
869          * 
870          * @param connection The connection to delete
871          */
872         private synchronized void deleteConnection(HttpConnection connection) {
873             
874             HostConfiguration connectionConfiguration = configurationForConnection(connection);
875 
876             if (LOG.isDebugEnabled()) {
877                 LOG.debug("Reclaiming connection, hostConfig=" + connectionConfiguration);
878             }
879 
880             connection.close();
881 
882             HostConnectionPool hostPool = getHostPool(connectionConfiguration);
883             
884             hostPool.freeConnections.remove(connection);
885             hostPool.numConnections--;
886             numConnections--;
887 
888             // remove the connection from the timeout handler
889             idleConnectionHandler.remove(connection);            
890         }
891         
892         /***
893          * Close and delete an old, unused connection to make room for a new one.
894          */
895         public synchronized void deleteLeastUsedConnection() {
896 
897             HttpConnection connection = (HttpConnection) freeConnections.removeFirst();
898 
899             if (connection != null) {
900                 deleteConnection(connection);
901             } else if (LOG.isDebugEnabled()) {
902                 LOG.debug("Attempted to reclaim an unused connection but there were none.");
903             }
904         }
905 
906         /***
907          * Notifies a waiting thread that a connection for the given configuration is 
908          * available.
909          * @param configuration the host config to use for notifying
910          * @see #notifyWaitingThread(HostConnectionPool)
911          */
912         public synchronized void notifyWaitingThread(HostConfiguration configuration) {
913             notifyWaitingThread(getHostPool(configuration));
914         }
915 
916         /***
917          * Notifies a waiting thread that a connection for the given configuration is 
918          * available.  This will wake a thread waiting in this host pool or if there is not
919          * one a thread in the connection pool will be notified.
920          * 
921          * @param hostPool the host pool to use for notifying
922          */
923         public synchronized void notifyWaitingThread(HostConnectionPool hostPool) {
924 
925             // find the thread we are going to notify, we want to ensure that each
926             // waiting thread is only interrupted once so we will remove it from 
927             // all wait queues before interrupting it
928             WaitingThread waitingThread = null;
929                 
930             if (hostPool.waitingThreads.size() > 0) {
931                 if (LOG.isDebugEnabled()) {
932                     LOG.debug("Notifying thread waiting on host pool, hostConfig=" 
933                         + hostPool.hostConfiguration);
934                 }                
935                 waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
936                 waitingThreads.remove(waitingThread);
937             } else if (waitingThreads.size() > 0) {
938                 if (LOG.isDebugEnabled()) {
939                     LOG.debug("No-one waiting on host pool, notifying next waiting thread.");
940                 }
941                 waitingThread = (WaitingThread) waitingThreads.removeFirst();
942                 waitingThread.hostConnectionPool.waitingThreads.remove(waitingThread);
943             } else if (LOG.isDebugEnabled()) {
944                 LOG.debug("Notifying no-one, there are no waiting threads");
945             }
946                 
947             if (waitingThread != null) {
948                 waitingThread.thread.interrupt();
949             }
950         }
951 
952         /***
953          * Marks the given connection as free.
954          * @param conn a connection that is no longer being used
955          */
956         public void freeConnection(HttpConnection conn) {
957 
958             HostConfiguration connectionConfiguration = configurationForConnection(conn);
959 
960             if (LOG.isDebugEnabled()) {
961                 LOG.debug("Freeing connection, hostConfig=" + connectionConfiguration);
962             }
963 
964             synchronized (this) {
965                 
966                 if (shutdown) {
967                     // the connection manager has been shutdown, release the connection's
968                     // resources and get out of here
969                     conn.close();
970                     return;
971                 }
972                 
973                 HostConnectionPool hostPool = getHostPool(connectionConfiguration);
974 
975                 // Put the connect back in the available list and notify a waiter
976                 hostPool.freeConnections.add(conn);
977                 if (hostPool.numConnections == 0) {
978                     // for some reason this connection pool didn't already exist
979                     LOG.error("Host connection pool not found, hostConfig=" 
980                               + connectionConfiguration);
981                     hostPool.numConnections = 1;
982                 }
983 
984                 freeConnections.add(conn);
985                 // we can remove the reference to this connection as we have control over
986                 // it again.  this also ensures that the connection manager can be GCed
987                 removeReferenceToConnection((HttpConnectionWithReference) conn);
988                 if (numConnections == 0) {
989                     // for some reason this connection pool didn't already exist
990                     LOG.error("Host connection pool not found, hostConfig=" 
991                               + connectionConfiguration);
992                     numConnections = 1;
993                 }
994 
995                 // register the connection with the timeout handler
996                 idleConnectionHandler.add(conn);
997 
998                 notifyWaitingThread(hostPool);
999             }
1000         }
1001     }
1002 
1003     /***
1004      * A simple struct-like class to combine the objects needed to release a connection's
1005      * resources when claimed by the garbage collector.
1006      */
1007     private static class ConnectionSource {
1008         
1009         /*** The connection pool that created the connection */
1010         public ConnectionPool connectionPool;
1011 
1012         /*** The connection's host configuration */
1013         public HostConfiguration hostConfiguration;
1014     }
1015     
1016     /***
1017      * A simple struct-like class to combine the connection list and the count
1018      * of created connections.
1019      */
1020     private static class HostConnectionPool {
1021         /*** The hostConfig this pool is for */
1022         public HostConfiguration hostConfiguration;
1023         
1024         /*** The list of free connections */
1025         public LinkedList freeConnections = new LinkedList();
1026         
1027         /*** The list of WaitingThreads for this host */
1028         public LinkedList waitingThreads = new LinkedList();
1029 
1030         /*** The number of created connections */
1031         public int numConnections = 0;
1032     }
1033     
1034     /***
1035      * A simple struct-like class to combine the waiting thread and the connection 
1036      * pool it is waiting on.
1037      */
1038     private static class WaitingThread {
1039         /*** The thread that is waiting for a connection */
1040         public Thread thread;
1041         
1042         /*** The connection pool the thread is waiting for */
1043         public HostConnectionPool hostConnectionPool;
1044     }
1045 
1046     /***
1047      * A thread for listening for HttpConnections reclaimed by the garbage
1048      * collector.
1049      */
1050     private static class ReferenceQueueThread extends Thread {
1051 
1052         private boolean shutdown = false;
1053         
1054         /***
1055          * Create an instance and make this a daemon thread.
1056          */
1057         public ReferenceQueueThread() {
1058             setDaemon(true);
1059             setName("MultiThreadedHttpConnectionManager cleanup");
1060         }
1061 
1062         public void shutdown() {
1063             this.shutdown = true;
1064         }
1065         
1066         /***
1067          * Handles cleaning up for the given connection reference.
1068          * 
1069          * @param ref the reference to clean up
1070          */
1071         private void handleReference(Reference ref) {
1072             
1073             ConnectionSource source = null;
1074             
1075             synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
1076                 source = (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.remove(ref);
1077             }
1078             // only clean up for this reference if it is still associated with 
1079             // a ConnectionSource
1080             if (source != null) {
1081                 if (LOG.isDebugEnabled()) {
1082                     LOG.debug(
1083                         "Connection reclaimed by garbage collector, hostConfig=" 
1084                         + source.hostConfiguration);
1085                 }
1086                 
1087                 source.connectionPool.handleLostConnection(source.hostConfiguration);
1088             }
1089         }
1090 
1091         /***
1092          * Start execution.
1093          */
1094         public void run() {
1095             while (!shutdown) {
1096                 try {
1097                     // remove the next reference and process it, a timeout 
1098                     // is used so that the thread does not block indefinitely 
1099                     // and therefore keep the thread from shutting down
1100                     Reference ref = REFERENCE_QUEUE.remove(1000);
1101                     if (ref != null) {
1102                         handleReference(ref);
1103                     }
1104                 } catch (InterruptedException e) {
1105                     LOG.debug("ReferenceQueueThread interrupted", e);
1106                 }
1107             }
1108         }
1109 
1110     }
1111     
1112     /***
1113      * A connection that keeps a reference to itself.
1114      */
1115     private static class HttpConnectionWithReference extends HttpConnection {
1116         
1117         public WeakReference reference = new WeakReference(this, REFERENCE_QUEUE);
1118         
1119         /***
1120          * @param hostConfiguration
1121          */
1122         public HttpConnectionWithReference(HostConfiguration hostConfiguration) {
1123             super(hostConfiguration);
1124         }
1125 
1126     }
1127     
1128     /***
1129      * An HttpConnection wrapper that ensures a connection cannot be used
1130      * once released.
1131      */
1132     private static class HttpConnectionAdapter extends HttpConnection {
1133 
1134         // the wrapped connection
1135         private HttpConnection wrappedConnection;
1136 
1137         /***
1138          * Creates a new HttpConnectionAdapter.
1139          * @param connection the connection to be wrapped
1140          */
1141         public HttpConnectionAdapter(HttpConnection connection) {
1142             super(connection.getHost(), connection.getPort(), connection.getProtocol());
1143             this.wrappedConnection = connection;
1144         }
1145 
1146         /***
1147          * Tests if the wrapped connection is still available.
1148          * @return boolean
1149          */
1150         protected boolean hasConnection() {
1151             return wrappedConnection != null;
1152         }
1153 
1154         /***
1155          * @return HttpConnection
1156          */
1157         HttpConnection getWrappedConnection() {
1158             return wrappedConnection;
1159         }
1160         
1161         public void close() {
1162             if (hasConnection()) {
1163                 wrappedConnection.close();
1164             } else {
1165                 // do nothing
1166             }
1167         }
1168 
1169         public InetAddress getLocalAddress() {
1170             if (hasConnection()) {
1171                 return wrappedConnection.getLocalAddress();
1172             } else {
1173                 return null;
1174             }
1175         }
1176 
1177         /***
1178          * @deprecated
1179          */
1180         public boolean isStaleCheckingEnabled() {
1181             if (hasConnection()) {
1182                 return wrappedConnection.isStaleCheckingEnabled();
1183             } else {
1184                 return false;
1185             }
1186         }
1187 
1188         public void setLocalAddress(InetAddress localAddress) {
1189             if (hasConnection()) {
1190                 wrappedConnection.setLocalAddress(localAddress);
1191             } else {
1192                 throw new IllegalStateException("Connection has been released");
1193             }
1194         }
1195     
1196         /***
1197          * @deprecated 
1198          */
1199         public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
1200             if (hasConnection()) {
1201                 wrappedConnection.setStaleCheckingEnabled(staleCheckEnabled);
1202             } else {
1203                 throw new IllegalStateException("Connection has been released");
1204             }
1205         }
1206 
1207         public String getHost() {
1208             if (hasConnection()) {
1209                 return wrappedConnection.getHost();
1210             } else {
1211                 return null;
1212             }
1213         }
1214 
1215         public HttpConnectionManager getHttpConnectionManager() {
1216             if (hasConnection()) {
1217                 return wrappedConnection.getHttpConnectionManager();
1218             } else {
1219                 return null;
1220             }
1221         }
1222 
1223         public InputStream getLastResponseInputStream() {
1224             if (hasConnection()) {
1225                 return wrappedConnection.getLastResponseInputStream();
1226             } else {
1227                 return null;
1228             }
1229         }
1230 
1231         public int getPort() {
1232             if (hasConnection()) {
1233                 return wrappedConnection.getPort();
1234             } else {
1235                 return -1;
1236             }
1237         }
1238 
1239         public Protocol getProtocol() {
1240             if (hasConnection()) {
1241                 return wrappedConnection.getProtocol();
1242             } else {
1243                 return null;
1244             }
1245         }
1246 
1247         public String getProxyHost() {
1248             if (hasConnection()) {
1249                 return wrappedConnection.getProxyHost();
1250             } else {
1251                 return null;
1252             }
1253         }
1254 
1255         public int getProxyPort() {
1256             if (hasConnection()) {
1257                 return wrappedConnection.getProxyPort();
1258             } else {
1259                 return -1;
1260             }
1261         }
1262 
1263         public OutputStream getRequestOutputStream()
1264             throws IOException, IllegalStateException {
1265             if (hasConnection()) {
1266                 return wrappedConnection.getRequestOutputStream();
1267             } else {
1268                 return null;
1269             }
1270         }
1271 
1272         public InputStream getResponseInputStream()
1273             throws IOException, IllegalStateException {
1274             if (hasConnection()) {
1275                 return wrappedConnection.getResponseInputStream();
1276             } else {
1277                 return null;
1278             }
1279         }
1280 
1281         public boolean isOpen() {
1282             if (hasConnection()) {
1283                 return wrappedConnection.isOpen();
1284             } else {
1285                 return false;
1286             }
1287         }
1288 
1289         public boolean closeIfStale() throws IOException {
1290             if (hasConnection()) {
1291                 return wrappedConnection.closeIfStale();
1292             } else {
1293                 return false;
1294             }
1295         }
1296 
1297         public boolean isProxied() {
1298             if (hasConnection()) {
1299                 return wrappedConnection.isProxied();
1300             } else {
1301                 return false;
1302             }
1303         }
1304 
1305         public boolean isResponseAvailable() throws IOException {
1306             if (hasConnection()) {
1307                 return  wrappedConnection.isResponseAvailable();
1308             } else {
1309                 return false;
1310             }
1311         }
1312 
1313         public boolean isResponseAvailable(int timeout) throws IOException {
1314             if (hasConnection()) {
1315                 return  wrappedConnection.isResponseAvailable(timeout);
1316             } else {
1317                 return false;
1318             }
1319         }
1320 
1321         public boolean isSecure() {
1322             if (hasConnection()) {
1323                 return wrappedConnection.isSecure();
1324             } else {
1325                 return false;
1326             }
1327         }
1328 
1329         public boolean isTransparent() {
1330             if (hasConnection()) {
1331                 return wrappedConnection.isTransparent();
1332             } else {
1333                 return false;
1334             }
1335         }
1336 
1337         public void open() throws IOException {
1338             if (hasConnection()) {
1339                 wrappedConnection.open();
1340             } else {
1341                 throw new IllegalStateException("Connection has been released");
1342             }
1343         }
1344 
1345         /***
1346          * @deprecated
1347          */
1348         public void print(String data)
1349             throws IOException, IllegalStateException {
1350             if (hasConnection()) {
1351                 wrappedConnection.print(data);
1352             } else {
1353                 throw new IllegalStateException("Connection has been released");
1354             }
1355         }
1356 
1357         public void printLine()
1358             throws IOException, IllegalStateException {
1359             if (hasConnection()) {
1360                 wrappedConnection.printLine();
1361             } else {
1362                 throw new IllegalStateException("Connection has been released");
1363             }
1364         }
1365 
1366         /***
1367          * @deprecated
1368          */
1369         public void printLine(String data)
1370             throws IOException, IllegalStateException {
1371             if (hasConnection()) {
1372                 wrappedConnection.printLine(data);
1373             } else {
1374                 throw new IllegalStateException("Connection has been released");
1375             }
1376         }
1377 
1378         /***
1379          * @deprecated
1380          */
1381         public String readLine() throws IOException, IllegalStateException {
1382             if (hasConnection()) {
1383                 return wrappedConnection.readLine();
1384             } else {
1385                 throw new IllegalStateException("Connection has been released");
1386             }
1387         }
1388 
1389         public String readLine(String charset) throws IOException, IllegalStateException {
1390             if (hasConnection()) {
1391                 return wrappedConnection.readLine(charset);
1392             } else {
1393                 throw new IllegalStateException("Connection has been released");
1394             }
1395         }
1396 
1397         public void releaseConnection() {
1398             if (!isLocked() && hasConnection()) {
1399                 HttpConnection wrappedConnection = this.wrappedConnection;
1400                 this.wrappedConnection = null;
1401                 wrappedConnection.releaseConnection();
1402             } else {
1403                 // do nothing
1404             }
1405         }
1406 
1407         /***
1408          * @deprecated
1409          */
1410         public void setConnectionTimeout(int timeout) {
1411             if (hasConnection()) {
1412                 wrappedConnection.setConnectionTimeout(timeout);
1413             } else {
1414                 // do nothing
1415             }
1416         }
1417 
1418         public void setHost(String host) throws IllegalStateException {
1419             if (hasConnection()) {
1420                 wrappedConnection.setHost(host);
1421             } else {
1422                 // do nothing
1423             }
1424         }
1425 
1426         public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1427             if (hasConnection()) {
1428                 wrappedConnection.setHttpConnectionManager(httpConnectionManager);
1429             } else {
1430                 // do nothing
1431             }
1432         }
1433 
1434         public void setLastResponseInputStream(InputStream inStream) {
1435             if (hasConnection()) {
1436                 wrappedConnection.setLastResponseInputStream(inStream);
1437             } else {
1438                 // do nothing
1439             }
1440         }
1441 
1442         public void setPort(int port) throws IllegalStateException {
1443             if (hasConnection()) {
1444                 wrappedConnection.setPort(port);
1445             } else {
1446                 // do nothing
1447             }
1448         }
1449 
1450         public void setProtocol(Protocol protocol) {
1451             if (hasConnection()) {
1452                 wrappedConnection.setProtocol(protocol);
1453             } else {
1454                 // do nothing
1455             }
1456         }
1457 
1458         public void setProxyHost(String host) throws IllegalStateException {
1459             if (hasConnection()) {
1460                 wrappedConnection.setProxyHost(host);
1461             } else {
1462                 // do nothing
1463             }
1464         }
1465 
1466         public void setProxyPort(int port) throws IllegalStateException {
1467             if (hasConnection()) {
1468                 wrappedConnection.setProxyPort(port);
1469             } else {
1470                 // do nothing
1471             }
1472         }
1473 
1474         /***
1475          * @deprecated
1476          */
1477         public void setSoTimeout(int timeout)
1478             throws SocketException, IllegalStateException {
1479             if (hasConnection()) {
1480                 wrappedConnection.setSoTimeout(timeout);
1481             } else {
1482                 // do nothing
1483             }
1484         }
1485 
1486         /***
1487          * @deprecated
1488          */
1489         public void shutdownOutput() {
1490             if (hasConnection()) {
1491                 wrappedConnection.shutdownOutput();
1492             } else {
1493                 // do nothing
1494             }
1495         }
1496 
1497         public void tunnelCreated() throws IllegalStateException, IOException {
1498             if (hasConnection()) {
1499                 wrappedConnection.tunnelCreated();
1500             } else {
1501                 // do nothing
1502             }
1503         }
1504 
1505         public void write(byte[] data, int offset, int length)
1506             throws IOException, IllegalStateException {
1507             if (hasConnection()) {
1508                 wrappedConnection.write(data, offset, length);
1509             } else {
1510                 throw new IllegalStateException("Connection has been released");
1511             }
1512         }
1513 
1514         public void write(byte[] data)
1515             throws IOException, IllegalStateException {
1516             if (hasConnection()) {
1517                 wrappedConnection.write(data);
1518             } else {
1519                 throw new IllegalStateException("Connection has been released");
1520             }
1521         }
1522 
1523         public void writeLine()
1524             throws IOException, IllegalStateException {
1525             if (hasConnection()) {
1526                 wrappedConnection.writeLine();
1527             } else {
1528                 throw new IllegalStateException("Connection has been released");
1529             }
1530         }
1531 
1532         public void writeLine(byte[] data)
1533             throws IOException, IllegalStateException {
1534             if (hasConnection()) {
1535                 wrappedConnection.writeLine(data);
1536             } else {
1537                 throw new IllegalStateException("Connection has been released");
1538             }
1539         }
1540 
1541         public void flushRequestOutputStream() throws IOException {
1542             if (hasConnection()) {
1543                 wrappedConnection.flushRequestOutputStream();
1544             } else {
1545                 throw new IllegalStateException("Connection has been released");
1546             }
1547         }
1548 
1549         /***
1550          * @deprecated
1551          */
1552         public int getSoTimeout() throws SocketException {
1553             if (hasConnection()) {
1554                 return wrappedConnection.getSoTimeout();
1555             } else {
1556                 throw new IllegalStateException("Connection has been released");
1557             }
1558         }
1559 
1560         /***
1561          * @deprecated
1562          */
1563         public String getVirtualHost() {
1564             if (hasConnection()) {
1565                 return wrappedConnection.getVirtualHost();
1566             } else {
1567                 throw new IllegalStateException("Connection has been released");
1568             }
1569         }
1570 
1571         /***
1572          * @deprecated
1573          */
1574         public void setVirtualHost(String host) throws IllegalStateException {
1575             if (hasConnection()) {
1576                 wrappedConnection.setVirtualHost(host);
1577             } else {
1578                 throw new IllegalStateException("Connection has been released");
1579             }
1580         }
1581 
1582         public int getSendBufferSize() throws SocketException {
1583             if (hasConnection()) {
1584                 return wrappedConnection.getSendBufferSize();
1585             } else {
1586                 throw new IllegalStateException("Connection has been released");
1587             }
1588         }
1589 
1590         /***
1591          * @deprecated
1592          */
1593         public void setSendBufferSize(int sendBufferSize) throws SocketException {
1594             if (hasConnection()) {
1595                 wrappedConnection.setSendBufferSize(sendBufferSize);
1596             } else {
1597                 throw new IllegalStateException("Connection has been released");
1598             }
1599         }
1600 
1601         public HttpConnectionParams getParams() {
1602             if (hasConnection()) {
1603                 return wrappedConnection.getParams();
1604             } else {
1605                 throw new IllegalStateException("Connection has been released");
1606             }
1607         }
1608 
1609         public void setParams(final HttpConnectionParams params) {
1610             if (hasConnection()) {
1611                 wrappedConnection.setParams(params);
1612             } else {
1613                 throw new IllegalStateException("Connection has been released");
1614             }
1615         }
1616 
1617         /* (non-Javadoc)
1618          * @see org.apache.commons.httpclient.HttpConnection#print(java.lang.String, java.lang.String)
1619          */
1620         public void print(String data, String charset) throws IOException, IllegalStateException {
1621             if (hasConnection()) {
1622                 wrappedConnection.print(data, charset);
1623             } else {
1624                 throw new IllegalStateException("Connection has been released");
1625             }
1626         }
1627 
1628         /* (non-Javadoc)
1629          * @see org.apache.commons.httpclient.HttpConnection#printLine(java.lang.String, java.lang.String)
1630          */
1631         public void printLine(String data, String charset)
1632             throws IOException, IllegalStateException {
1633             if (hasConnection()) {
1634                 wrappedConnection.printLine(data, charset);
1635             } else {
1636                 throw new IllegalStateException("Connection has been released");
1637             }
1638         }
1639 
1640         /* (non-Javadoc)
1641          * @see org.apache.commons.httpclient.HttpConnection#setSocketTimeout(int)
1642          */
1643         public void setSocketTimeout(int timeout) throws SocketException, IllegalStateException {
1644             if (hasConnection()) {
1645                 wrappedConnection.setSocketTimeout(timeout);
1646             } else {
1647                 throw new IllegalStateException("Connection has been released");
1648             }
1649         }
1650 
1651     }
1652 
1653 }
1654