View Javadoc

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