View Javadoc

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