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