1 /*
2 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v 1.17.2.4 2003/09/01 18:05:51 mbecke Exp $
3 * $Revision: 1.17.2.4 $
4 * $Date: 2003/09/01 18:05:51 $
5 *
6 * ====================================================================
7 *
8 * The Apache Software License, Version 1.1
9 *
10 * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
11 * reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in
22 * the documentation and/or other materials provided with the
23 * distribution.
24 *
25 * 3. The end-user documentation included with the redistribution, if
26 * any, must include the following acknowlegement:
27 * "This product includes software developed by the
28 * Apache Software Foundation (http://www.apache.org/)."
29 * Alternately, this acknowlegement may appear in the software itself,
30 * if and wherever such third-party acknowlegements normally appear.
31 *
32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
33 * Foundation" must not be used to endorse or promote products derived
34 * from this software without prior written permission. For written
35 * permission, please contact apache@apache.org.
36 *
37 * 5. Products derived from this software may not be called "Apache"
38 * nor may "Apache" appear in their names without prior written
39 * permission of the Apache Group.
40 *
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 * ====================================================================
54 *
55 * This software consists of voluntary contributions made by many
56 * individuals on behalf of the Apache Software Foundation. For more
57 * information on the Apache Software Foundation, please see
58 * <http://www.apache.org/>.
59 *
60 * [Additional notices, if required by prior licensing conditions]
61 *
62 */
63
64 package org.apache.commons.httpclient;
65
66 import java.io.IOException;
67 import java.io.InputStream;
68 import java.io.OutputStream;
69 import java.lang.ref.Reference;
70 import java.lang.ref.ReferenceQueue;
71 import java.lang.ref.WeakReference;
72 import java.net.InetAddress;
73 import java.net.SocketException;
74 import java.util.Collections;
75 import java.util.HashMap;
76 import java.util.Iterator;
77 import java.util.LinkedList;
78 import java.util.Map;
79
80 import org.apache.commons.httpclient.protocol.Protocol;
81 import org.apache.commons.logging.Log;
82 import org.apache.commons.logging.LogFactory;
83
84 /***
85 * Manages a set of HttpConnections for various HostConfigurations.
86 *
87 * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
88 * @author Eric Johnson
89 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
90 * @author Carl A. Dunham
91 *
92 * @since 2.0
93 */
94 public class MultiThreadedHttpConnectionManager implements HttpConnectionManager {
95
96 // -------------------------------------------------------- Class Variables
97 /*** Log object for this class. */
98 private static final Log LOG = LogFactory.getLog(MultiThreadedHttpConnectionManager.class);
99
100 /*** The default maximum number of connections allowed per host */
101 public static final int DEFAULT_MAX_HOST_CONNECTIONS = 2; // Per RFC 2616 sec 8.1.4
102
103 /*** The default maximum number of connections allowed overall */
104 public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;
105
106 // ----------------------------------------------------- Instance Variables
107 /*** Maximum number of connections allowed per host */
108 private int maxHostConnections = DEFAULT_MAX_HOST_CONNECTIONS;
109
110 /*** Maximum number of connections allowed overall */
111 private int maxTotalConnections = DEFAULT_MAX_TOTAL_CONNECTIONS;
112
113 /*** The value to set when calling setStaleCheckingEnabled() on each connection */
114 private boolean connectionStaleCheckingEnabled = true;
115
116 /*** Connection Pool */
117 private ConnectionPool connectionPool;
118
119 /*** mapping from reference to hostConfiguration */
120 private Map referenceToHostConfig;
121
122 /***
123 * the reference queue used to track when HttpConnections are lost to the
124 * garbage collector
125 */
126 private ReferenceQueue referenceQueue;
127
128 /***
129 * No-args constructor
130 */
131 public MultiThreadedHttpConnectionManager() {
132
133 this.referenceToHostConfig = Collections.synchronizedMap(new HashMap());
134 this.connectionPool = new ConnectionPool();
135
136 this.referenceQueue = new ReferenceQueue();
137
138 new ReferenceQueueThread().start();
139 }
140
141 /***
142 * Gets the staleCheckingEnabled value to be set on HttpConnections that are created.
143 *
144 * @return <code>true</code> if stale checking will be enabled on HttpConections
145 *
146 * @see HttpConnection#isStaleCheckingEnabled()
147 */
148 public boolean isConnectionStaleCheckingEnabled() {
149 return connectionStaleCheckingEnabled;
150 }
151
152 /***
153 * Sets the staleCheckingEnabled value to be set on HttpConnections that are created.
154 *
155 * @param connectionStaleCheckingEnabled <code>true</code> if stale checking will be enabled
156 * on HttpConections
157 *
158 * @see HttpConnection#setStaleCheckingEnabled(boolean)
159 */
160 public void setConnectionStaleCheckingEnabled(boolean connectionStaleCheckingEnabled) {
161 this.connectionStaleCheckingEnabled = connectionStaleCheckingEnabled;
162 }
163
164 /***
165 * Sets the maximum number of connections allowed for a given
166 * HostConfiguration. Per RFC 2616 section 8.1.4, this value defaults to 2.
167 *
168 * @param maxHostConnections the number of connections allowed for each
169 * hostConfiguration
170 */
171 public void setMaxConnectionsPerHost(int maxHostConnections) {
172 this.maxHostConnections = maxHostConnections;
173 }
174
175 /***
176 * Gets the maximum number of connections allowed for a given
177 * hostConfiguration.
178 *
179 * @return The maximum number of connections allowed for a given
180 * hostConfiguration.
181 */
182 public int getMaxConnectionsPerHost() {
183 return maxHostConnections;
184 }
185
186 /***
187 * Sets the maximum number of connections allowed in the system.
188 *
189 * @param maxTotalConnections the maximum number of connections allowed
190 */
191 public void setMaxTotalConnections(int maxTotalConnections) {
192 this.maxTotalConnections = maxTotalConnections;
193 }
194
195 /***
196 * Gets the maximum number of connections allowed in the system.
197 *
198 * @return The maximum number of connections allowed
199 */
200 public int getMaxTotalConnections() {
201 return maxTotalConnections;
202 }
203
204 /***
205 * @see HttpConnectionManager#getConnection(HostConfiguration)
206 */
207 public HttpConnection getConnection(HostConfiguration hostConfiguration) {
208
209 while (true) {
210 try {
211 return getConnection(hostConfiguration, 0);
212 } catch (HttpException e) {
213 // we'll go ahead and log this, but it should never happen. HttpExceptions
214 // are only thrown when the timeout occurs and since we have no timeout
215 // it should never happen.
216 LOG.debug(
217 "Unexpected exception while waiting for connection",
218 e
219 );
220 };
221 }
222 }
223
224 /***
225 * @see HttpConnectionManager#getConnection(HostConfiguration, long)
226 */
227 public HttpConnection getConnection(HostConfiguration hostConfiguration,
228 long timeout) throws HttpException {
229
230 LOG.trace("enter HttpConnectionManager.getConnection(HostConfiguration, long)");
231
232 if (hostConfiguration == null) {
233 throw new IllegalArgumentException("hostConfiguration is null");
234 }
235
236 if (LOG.isDebugEnabled()) {
237 LOG.debug("HttpConnectionManager.getConnection: config = "
238 + hostConfiguration + ", timeout = " + timeout);
239 }
240
241 final HttpConnection conn = doGetConnection(hostConfiguration, timeout);
242
243 // wrap the connection in an adapter so we can ensure it is used
244 // only once
245 return new HttpConnectionAdapter(conn);
246 }
247
248 /***
249 * Gets a connection or waits if one is not available. A connection is
250 * available if one exists that is not being used or if fewer than
251 * maxHostConnections have been created in the connectionPool, and fewer
252 * than maxTotalConnections have been created in all connectionPools.
253 *
254 * @param hostConfiguration The host configuration.
255 * @param timeout the number of milliseconds to wait for a connection, 0 to
256 * wait indefinitely
257 *
258 * @return HttpConnection an available connection
259 *
260 * @throws HttpException if a connection does not become available in
261 * 'timeout' milliseconds
262 */
263 private HttpConnection doGetConnection(HostConfiguration hostConfiguration,
264 long timeout) throws HttpException {
265
266 HttpConnection connection = null;
267
268 synchronized (connectionPool) {
269
270 // we clone the hostConfiguration
271 // so that it cannot be changed once the connection has been retrieved
272 hostConfiguration = new HostConfiguration(hostConfiguration);
273 HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration);
274 WaitingThread waitingThread = null;
275
276 boolean useTimeout = (timeout > 0);
277 long timeToWait = timeout;
278 long startWait = 0;
279 long endWait = 0;
280
281 while (connection == null) {
282
283 // happen to have a free connection with the right specs
284 //
285 if (hostPool.freeConnections.size() > 0) {
286 connection = connectionPool.getFreeConnection(hostConfiguration);
287
288 // have room to make more
289 //
290 } else if ((hostPool.numConnections < maxHostConnections)
291 && (connectionPool.numConnections < maxTotalConnections)) {
292
293 connection = connectionPool.createConnection(hostConfiguration);
294
295 // have room to add host connection, and there is at least one free
296 // connection that can be liberated to make overall room
297 //
298 } else if ((hostPool.numConnections < maxHostConnections)
299 && (connectionPool.freeConnections.size() > 0)) {
300
301 connectionPool.deleteLeastUsedConnection();
302 connection = connectionPool.createConnection(hostConfiguration);
303
304 // otherwise, we have to wait for one of the above conditions to
305 // become true
306 //
307 } else {
308 // TODO: keep track of which hostConfigurations have waiting
309 // threads, so they avoid being sacrificed before necessary
310
311 try {
312
313 if (useTimeout && timeToWait <= 0) {
314 throw new HttpException("Timeout waiting for connection");
315 }
316
317 if (LOG.isDebugEnabled()) {
318 LOG.debug("Unable to get a connection, waiting..., hostConfig=" + hostConfiguration);
319 }
320
321 if (waitingThread == null) {
322 waitingThread = new WaitingThread();
323 waitingThread.hostConnectionPool = hostPool;
324 waitingThread.thread = Thread.currentThread();
325 }
326
327 if (useTimeout) {
328 startWait = System.currentTimeMillis();
329 }
330
331 hostPool.waitingThreads.addLast(waitingThread);
332 connectionPool.waitingThreads.addLast(waitingThread);
333 connectionPool.wait(timeToWait);
334
335 // we have not been interrupted so we need to remove ourselves from the
336 // wait queue
337 hostPool.waitingThreads.remove(waitingThread);
338 connectionPool.waitingThreads.remove(waitingThread);
339 } catch (InterruptedException e) {
340 // do nothing
341 } finally {
342 if (useTimeout) {
343 endWait = System.currentTimeMillis();
344 timeToWait -= (endWait - startWait);
345 }
346 }
347 }
348 }
349 }
350 return connection;
351 }
352
353 /***
354 * Gets the number of connections in use for this configuration.
355 *
356 * @param hostConfiguration the key that connections are tracked on
357 * @return the number of connections in use
358 */
359 public int getConnectionsInUse(HostConfiguration hostConfiguration) {
360 synchronized (connectionPool) {
361 HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration);
362 return hostPool.numConnections;
363 }
364 }
365
366 /***
367 * Gets the total number of connections in use.
368 *
369 * @return the total number of connections in use
370 */
371 public int getConnectionsInUse() {
372 synchronized (connectionPool) {
373 return connectionPool.numConnections;
374 }
375 }
376
377 /***
378 * Make the given HttpConnection available for use by other requests.
379 * If another thread is blocked in getConnection() that could use this
380 * connection, it will be woken up.
381 *
382 * @param conn the HttpConnection to make available.
383 */
384 public void releaseConnection(HttpConnection conn) {
385 LOG.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
386
387 if (conn instanceof HttpConnectionAdapter) {
388 // connections given out are wrapped in an HttpConnectionAdapter
389 conn = ((HttpConnectionAdapter) conn).getWrappedConnection();
390 } else {
391 // this is okay, when an HttpConnectionAdapter is released
392 // is releases the real connection
393 }
394
395 // make sure that the response has been read.
396 SimpleHttpConnectionManager.finishLastResponse(conn);
397
398 connectionPool.freeConnection(conn);
399 }
400
401 /***
402 * Gets the host configuration for a connection.
403 * @param conn the connection to get the configuration of
404 * @return a new HostConfiguration
405 */
406 private HostConfiguration configurationForConnection(HttpConnection conn) {
407
408 HostConfiguration connectionConfiguration = new HostConfiguration();
409
410 connectionConfiguration.setHost(
411 conn.getHost(),
412 conn.getVirtualHost(),
413 conn.getPort(),
414 conn.getProtocol()
415 );
416 if (conn.getLocalAddress() != null) {
417 connectionConfiguration.setLocalAddress(conn.getLocalAddress());
418 }
419 if (conn.getProxyHost() != null) {
420 connectionConfiguration.setProxy(conn.getProxyHost(), conn.getProxyPort());
421 }
422
423 return connectionConfiguration;
424 }
425
426
427 /***
428 * Global Connection Pool, including per-host pools
429 */
430 private class ConnectionPool {
431
432 /*** The list of free connections */
433 private LinkedList freeConnections = new LinkedList();
434
435 /*** The list of WaitingThreads waiting for a connection */
436 private LinkedList waitingThreads = new LinkedList();
437
438 /***
439 * Map where keys are {@link HostConfiguration}s and values are {@link
440 * HostConnectionPool}s
441 */
442 private final Map mapHosts = new HashMap();
443
444 /*** The number of created connections */
445 private int numConnections = 0;
446
447 /***
448 * Creates a new connection and returns is for use of the calling method.
449 *
450 * @param hostConfiguration the configuration for the connection
451 * @return a new connection or <code>null</code> if none are available
452 */
453 public synchronized HttpConnection createConnection(HostConfiguration hostConfiguration) {
454 HttpConnection connection = null;
455
456 HostConnectionPool hostPool = getHostPool(hostConfiguration);
457
458 if ((hostPool.numConnections < getMaxConnectionsPerHost())
459 && (numConnections < getMaxTotalConnections())) {
460
461 if (LOG.isDebugEnabled()) {
462 LOG.debug("Allocating new connection, hostConfig=" + hostConfiguration);
463 }
464 connection = new HttpConnection(hostConfiguration);
465 connection.setStaleCheckingEnabled(connectionStaleCheckingEnabled);
466 connection.setHttpConnectionManager(MultiThreadedHttpConnectionManager.this);
467 numConnections++;
468 hostPool.numConnections++;
469
470 // add a weak reference to this connection
471 referenceToHostConfig.put(new WeakReference(connection, referenceQueue),
472 hostConfiguration);
473 } else if (LOG.isDebugEnabled()) {
474 if (hostPool.numConnections >= getMaxConnectionsPerHost()) {
475 LOG.debug("No connection allocated, host pool has already reached "
476 + "maxConnectionsPerHost, hostConfig=" + hostConfiguration
477 + ", maxConnectionsPerhost=" + getMaxConnectionsPerHost());
478 } else {
479 LOG.debug("No connection allocated, maxTotalConnections reached, "
480 + "maxTotalConnections=" + getMaxTotalConnections());
481 }
482 }
483
484 return connection;
485 }
486
487 /***
488 * Get the pool (list) of connections available for the given hostConfig.
489 *
490 * @param hostConfiguration the configuraton for the connection pool
491 * @return a pool (list) of connections available for the given config
492 */
493 public synchronized HostConnectionPool getHostPool(HostConfiguration hostConfiguration) {
494 LOG.trace("enter HttpConnectionManager.ConnectionPool.getHostPool(HostConfiguration)");
495
496 // Look for a list of connections for the given config
497 HostConnectionPool listConnections = (HostConnectionPool)
498 mapHosts.get(hostConfiguration);
499 if (listConnections == null) {
500 // First time for this config
501 listConnections = new HostConnectionPool();
502 listConnections.hostConfiguration = hostConfiguration;
503 mapHosts.put(hostConfiguration, listConnections);
504 }
505
506 return listConnections;
507 }
508
509 /***
510 * If available, get a free connection for this host
511 *
512 * @param hostConfiguration the configuraton for the connection pool
513 * @return an available connection for the given config
514 */
515 public synchronized HttpConnection getFreeConnection(HostConfiguration hostConfiguration) {
516
517 HttpConnection connection = null;
518
519 HostConnectionPool hostPool = getHostPool(hostConfiguration);
520
521 if (hostPool.freeConnections.size() > 0) {
522 connection = (HttpConnection) hostPool.freeConnections.removeFirst();
523 freeConnections.remove(connection);
524 if (LOG.isDebugEnabled()) {
525 LOG.debug("Getting free connection, hostConfig=" + hostConfiguration);
526 }
527 } else if (LOG.isDebugEnabled()) {
528 LOG.debug("There were no free connections to get, hostConfig="
529 + hostConfiguration);
530 }
531 return connection;
532 }
533
534 /***
535 * Close and delete an old, unused connection to make room for a new one.
536 */
537 public synchronized void deleteLeastUsedConnection() {
538
539 HttpConnection connection = (HttpConnection) freeConnections.removeFirst();
540
541 if (connection != null) {
542 HostConfiguration connectionConfiguration = configurationForConnection(connection);
543
544 if (LOG.isDebugEnabled()) {
545 LOG.debug("Reclaiming unused connection, hostConfig="
546 + connectionConfiguration);
547 }
548
549 connection.close();
550
551 // make sure this connection will not be cleaned up again when garbage
552 // collected
553 for (Iterator iter = referenceToHostConfig.keySet().iterator(); iter.hasNext();) {
554 WeakReference connectionRef = (WeakReference) iter.next();
555 if (connectionRef.get() == connection) {
556 iter.remove();
557 connectionRef.enqueue();
558 break;
559 }
560 }
561
562 HostConnectionPool hostPool = getHostPool(connectionConfiguration);
563
564 hostPool.freeConnections.remove(connection);
565 hostPool.numConnections--;
566 numConnections--;
567 } else if (LOG.isDebugEnabled()) {
568 LOG.debug("Attempted to reclaim an unused connection but there were none.");
569 }
570 }
571
572 /***
573 * Notifies a waiting thread that a connection for the given configuration is
574 * available.
575 * @param configuration the host config to use for notifying
576 * @see #notifyWaitingThread(HostConnectionPool)
577 */
578 public synchronized void notifyWaitingThread(HostConfiguration configuration) {
579 notifyWaitingThread(getHostPool(configuration));
580 }
581
582 /***
583 * Notifies a waiting thread that a connection for the given configuration is
584 * available. This will wake a thread witing in tis hostPool or if there is not
585 * one a thread in the ConnectionPool will be notified.
586 *
587 * @param hostPool the host pool to use for notifying
588 */
589 public synchronized void notifyWaitingThread(HostConnectionPool hostPool) {
590
591 // find the thread we are going to notify, we want to ensure that each
592 // waiting thread is only interrupted once so we will remove it from
593 // all wait queues before interrupting it
594 WaitingThread waitingThread = null;
595
596 if (hostPool.waitingThreads.size() > 0) {
597 if (LOG.isDebugEnabled()) {
598 LOG.debug("Notifying thread waiting on host pool, hostConfig="
599 + hostPool.hostConfiguration);
600 }
601 waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
602 waitingThreads.remove(waitingThread);
603 } else if (waitingThreads.size() > 0) {
604 if (LOG.isDebugEnabled()) {
605 LOG.debug("No-one waiting on host pool, notifying next waiting thread.");
606 }
607 waitingThread = (WaitingThread) waitingThreads.removeFirst();
608 waitingThread.hostConnectionPool.waitingThreads.remove(waitingThread);
609 } else if (LOG.isDebugEnabled()) {
610 LOG.debug("Notifying no-one, there are no waiting threads");
611 }
612
613 if (waitingThread != null) {
614 waitingThread.thread.interrupt();
615 }
616 }
617
618 /***
619 * Marks the given connection as free.
620 * @param conn a connection that is no longer being used
621 */
622 public void freeConnection(HttpConnection conn) {
623
624 HostConfiguration connectionConfiguration = configurationForConnection(conn);
625
626 if (LOG.isDebugEnabled()) {
627 LOG.debug("Freeing connection, hostConfig=" + connectionConfiguration);
628 }
629
630 synchronized (this) {
631 HostConnectionPool hostPool = getHostPool(connectionConfiguration);
632
633 // Put the connect back in the available list and notify a waiter
634 hostPool.freeConnections.add(conn);
635 if (hostPool.numConnections == 0) {
636 // for some reason this connection pool didn't already exist
637 LOG.error("Host connection pool not found, hostConfig="
638 + connectionConfiguration);
639 hostPool.numConnections = 1;
640 }
641
642 freeConnections.add(conn);
643 if (numConnections == 0) {
644 // for some reason this connection pool didn't already exist
645 LOG.error("Host connection pool not found, hostConfig="
646 + connectionConfiguration);
647 numConnections = 1;
648 }
649
650 notifyWaitingThread(hostPool);
651 }
652 }
653 }
654
655 /***
656 * A simple struct-like class to combine the connection list and the count
657 * of created connections.
658 */
659 private class HostConnectionPool {
660 /*** The hostConfig this pool is for */
661 public HostConfiguration hostConfiguration;
662
663 /*** The list of free connections */
664 public LinkedList freeConnections = new LinkedList();
665
666 /*** The list of WaitingThreads for this host */
667 public LinkedList waitingThreads = new LinkedList();
668
669 /*** The number of created connections */
670 public int numConnections = 0;
671 }
672
673 /***
674 * A simple struct-like class to combine the waiting thread and the connection
675 * pool it is waiting on.
676 */
677 private class WaitingThread {
678 /*** The thread that is waiting for a connection */
679 public Thread thread;
680
681 /*** The connection pool the thread is waiting for */
682 public HostConnectionPool hostConnectionPool;
683 }
684
685 /***
686 * A thread for listening for HttpConnections reclaimed by the garbage
687 * collector.
688 */
689 private class ReferenceQueueThread extends Thread {
690
691 /***
692 * Create an instance and make this a daemon thread.
693 */
694 public ReferenceQueueThread() {
695 setDaemon(true);
696 }
697
698 /***
699 * Handles cleaning up for the given reference. Decrements any connection counts
700 * and notifies waiting threads, if appropriate.
701 *
702 * @param ref the reference to clean up
703 */
704 private void handleReference(Reference ref) {
705 synchronized (connectionPool) {
706 // only clean up for this reference if it is still associated with
707 // a HostConfiguration
708 if (referenceToHostConfig.containsKey(ref)) {
709 HostConfiguration config = (HostConfiguration) referenceToHostConfig.get(ref);
710 referenceToHostConfig.remove(ref);
711
712 if (LOG.isDebugEnabled()) {
713 LOG.debug(
714 "Connection reclaimed by garbage collector, hostConfig=" + config);
715 }
716
717 HostConnectionPool hostPool = connectionPool.getHostPool(config);
718 hostPool.numConnections--;
719
720 connectionPool.numConnections--;
721 connectionPool.notifyWaitingThread(config);
722 }
723 }
724 }
725
726 /***
727 * Start execution.
728 */
729 public void run() {
730 while (true) {
731 try {
732 Reference ref = referenceQueue.remove();
733 if (ref != null) {
734 handleReference(ref);
735 }
736 } catch (InterruptedException e) {
737 LOG.debug("ReferenceQueueThread interrupted", e);
738 }
739 }
740 }
741
742 }
743
744 /***
745 * An HttpConnection wrapper that ensures a connection cannot be used
746 * once released.
747 */
748 private static class HttpConnectionAdapter extends HttpConnection {
749
750 // the wrapped connection
751 private HttpConnection wrappedConnection;
752
753 /***
754 * Creates a new HttpConnectionAdapter.
755 * @param connection the connection to be wrapped
756 */
757 public HttpConnectionAdapter(HttpConnection connection) {
758 super(connection.getHost(), connection.getPort(), connection.getProtocol());
759 this.wrappedConnection = connection;
760 }
761
762 /***
763 * Tests if the wrapped connection is still available.
764 * @return boolean
765 */
766 protected boolean hasConnection() {
767 return wrappedConnection != null;
768 }
769
770 /***
771 * @return HttpConnection
772 */
773 HttpConnection getWrappedConnection() {
774 return wrappedConnection;
775 }
776
777 public void close() {
778 if (hasConnection()) {
779 wrappedConnection.close();
780 } else {
781 // do nothing
782 }
783 }
784
785 public InetAddress getLocalAddress() {
786 if (hasConnection()) {
787 return wrappedConnection.getLocalAddress();
788 } else {
789 return null;
790 }
791 }
792
793 public boolean isStaleCheckingEnabled() {
794 if (hasConnection()) {
795 return wrappedConnection.isStaleCheckingEnabled();
796 } else {
797 return false;
798 }
799 }
800
801 public void setLocalAddress(InetAddress localAddress) {
802 if (hasConnection()) {
803 wrappedConnection.setLocalAddress(localAddress);
804 } else {
805 throw new IllegalStateException("Connection has been released");
806 }
807 }
808
809 public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
810 if (hasConnection()) {
811 wrappedConnection.setStaleCheckingEnabled(staleCheckEnabled);
812 } else {
813 throw new IllegalStateException("Connection has been released");
814 }
815 }
816
817 public String getHost() {
818 if (hasConnection()) {
819 return wrappedConnection.getHost();
820 } else {
821 return null;
822 }
823 }
824
825 public HttpConnectionManager getHttpConnectionManager() {
826 if (hasConnection()) {
827 return wrappedConnection.getHttpConnectionManager();
828 } else {
829 return null;
830 }
831 }
832
833 public InputStream getLastResponseInputStream() {
834 if (hasConnection()) {
835 return wrappedConnection.getLastResponseInputStream();
836 } else {
837 return null;
838 }
839 }
840
841 public int getPort() {
842 if (hasConnection()) {
843 return wrappedConnection.getPort();
844 } else {
845 return -1;
846 }
847 }
848
849 public Protocol getProtocol() {
850 if (hasConnection()) {
851 return wrappedConnection.getProtocol();
852 } else {
853 return null;
854 }
855 }
856
857 public String getProxyHost() {
858 if (hasConnection()) {
859 return wrappedConnection.getProxyHost();
860 } else {
861 return null;
862 }
863 }
864
865 public int getProxyPort() {
866 if (hasConnection()) {
867 return wrappedConnection.getProxyPort();
868 } else {
869 return -1;
870 }
871 }
872
873 public OutputStream getRequestOutputStream()
874 throws IOException, IllegalStateException {
875 if (hasConnection()) {
876 return wrappedConnection.getRequestOutputStream();
877 } else {
878 return null;
879 }
880 }
881
882 public OutputStream getRequestOutputStream(boolean useChunking)
883 throws IOException, IllegalStateException {
884 if (hasConnection()) {
885 return wrappedConnection.getRequestOutputStream(useChunking);
886 } else {
887 return null;
888 }
889 }
890
891 public InputStream getResponseInputStream()
892 throws IOException, IllegalStateException {
893 if (hasConnection()) {
894 return wrappedConnection.getResponseInputStream();
895 } else {
896 return null;
897 }
898 }
899
900 public InputStream getResponseInputStream(HttpMethod method)
901 throws IOException, IllegalStateException {
902 if (hasConnection()) {
903 return wrappedConnection.getResponseInputStream(method);
904 } else {
905 return null;
906 }
907 }
908
909 public boolean isOpen() {
910 if (hasConnection()) {
911 return wrappedConnection.isOpen();
912 } else {
913 return false;
914 }
915 }
916
917 public boolean isProxied() {
918 if (hasConnection()) {
919 return wrappedConnection.isProxied();
920 } else {
921 return false;
922 }
923 }
924
925 public boolean isResponseAvailable() throws IOException {
926 if (hasConnection()) {
927 return wrappedConnection.isResponseAvailable();
928 } else {
929 return false;
930 }
931 }
932
933 public boolean isResponseAvailable(int timeout) throws IOException {
934 if (hasConnection()) {
935 return wrappedConnection.isResponseAvailable(timeout);
936 } else {
937 return false;
938 }
939 }
940
941 public boolean isSecure() {
942 if (hasConnection()) {
943 return wrappedConnection.isSecure();
944 } else {
945 return false;
946 }
947 }
948
949 public boolean isTransparent() {
950 if (hasConnection()) {
951 return wrappedConnection.isTransparent();
952 } else {
953 return false;
954 }
955 }
956
957 public void open() throws IOException {
958 if (hasConnection()) {
959 wrappedConnection.open();
960 } else {
961 throw new IllegalStateException("Connection has been released");
962 }
963 }
964
965 public void print(String data)
966 throws IOException, IllegalStateException, HttpRecoverableException {
967 if (hasConnection()) {
968 wrappedConnection.print(data);
969 } else {
970 throw new IllegalStateException("Connection has been released");
971 }
972 }
973
974 public void printLine()
975 throws IOException, IllegalStateException, HttpRecoverableException {
976 if (hasConnection()) {
977 wrappedConnection.printLine();
978 } else {
979 throw new IllegalStateException("Connection has been released");
980 }
981 }
982
983 public void printLine(String data)
984 throws IOException, IllegalStateException, HttpRecoverableException {
985 if (hasConnection()) {
986 wrappedConnection.printLine(data);
987 } else {
988 throw new IllegalStateException("Connection has been released");
989 }
990 }
991
992 public String readLine() throws IOException, IllegalStateException {
993 if (hasConnection()) {
994 return wrappedConnection.readLine();
995 } else {
996 throw new IllegalStateException("Connection has been released");
997 }
998 }
999
1000 public void releaseConnection() {
1001 if (hasConnection()) {
1002 HttpConnection wrappedConnection = this.wrappedConnection;
1003 this.wrappedConnection = null;
1004 wrappedConnection.releaseConnection();
1005 } else {
1006 // do nothing
1007 }
1008 }
1009
1010 public void setConnectionTimeout(int timeout) {
1011 if (hasConnection()) {
1012 wrappedConnection.setConnectionTimeout(timeout);
1013 } else {
1014 // do nothing
1015 }
1016 }
1017
1018 public void setHost(String host) throws IllegalStateException {
1019 if (hasConnection()) {
1020 wrappedConnection.setHost(host);
1021 } else {
1022 // do nothing
1023 }
1024 }
1025
1026 public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1027 if (hasConnection()) {
1028 wrappedConnection.setHttpConnectionManager(httpConnectionManager);
1029 } else {
1030 // do nothing
1031 }
1032 }
1033
1034 public void setLastResponseInputStream(InputStream inStream) {
1035 if (hasConnection()) {
1036 wrappedConnection.setLastResponseInputStream(inStream);
1037 } else {
1038 // do nothing
1039 }
1040 }
1041
1042 public void setPort(int port) throws IllegalStateException {
1043 if (hasConnection()) {
1044 wrappedConnection.setPort(port);
1045 } else {
1046 // do nothing
1047 }
1048 }
1049
1050 public void setProtocol(Protocol protocol) {
1051 if (hasConnection()) {
1052 wrappedConnection.setProtocol(protocol);
1053 } else {
1054 // do nothing
1055 }
1056 }
1057
1058 public void setProxyHost(String host) throws IllegalStateException {
1059 if (hasConnection()) {
1060 wrappedConnection.setProxyHost(host);
1061 } else {
1062 // do nothing
1063 }
1064 }
1065
1066 public void setProxyPort(int port) throws IllegalStateException {
1067 if (hasConnection()) {
1068 wrappedConnection.setProxyPort(port);
1069 } else {
1070 // do nothing
1071 }
1072 }
1073
1074 public void setSecure(boolean secure) throws IllegalStateException {
1075 if (hasConnection()) {
1076 wrappedConnection.setSecure(secure);
1077 } else {
1078 // do nothing
1079 }
1080 }
1081
1082 public void setSoTimeout(int timeout)
1083 throws SocketException, IllegalStateException {
1084 if (hasConnection()) {
1085 wrappedConnection.setSoTimeout(timeout);
1086 } else {
1087 // do nothing
1088 }
1089 }
1090
1091 public void shutdownOutput() {
1092 if (hasConnection()) {
1093 wrappedConnection.shutdownOutput();
1094 } else {
1095 // do nothing
1096 }
1097 }
1098
1099 public void tunnelCreated() throws IllegalStateException, IOException {
1100 if (hasConnection()) {
1101 wrappedConnection.tunnelCreated();
1102 } else {
1103 // do nothing
1104 }
1105 }
1106
1107 public void write(byte[] data, int offset, int length)
1108 throws IOException, IllegalStateException, HttpRecoverableException {
1109 if (hasConnection()) {
1110 wrappedConnection.write(data, offset, length);
1111 } else {
1112 throw new IllegalStateException("Connection has been released");
1113 }
1114 }
1115
1116 public void write(byte[] data)
1117 throws IOException, IllegalStateException, HttpRecoverableException {
1118 if (hasConnection()) {
1119 wrappedConnection.write(data);
1120 } else {
1121 throw new IllegalStateException("Connection has been released");
1122 }
1123 }
1124
1125 public void writeLine()
1126 throws IOException, IllegalStateException, HttpRecoverableException {
1127 if (hasConnection()) {
1128 wrappedConnection.writeLine();
1129 } else {
1130 throw new IllegalStateException("Connection has been released");
1131 }
1132 }
1133
1134 public void writeLine(byte[] data)
1135 throws IOException, IllegalStateException, HttpRecoverableException {
1136 if (hasConnection()) {
1137 wrappedConnection.writeLine(data);
1138 } else {
1139 throw new IllegalStateException("Connection has been released");
1140 }
1141 }
1142
1143 public void flushRequestOutputStream() throws IOException {
1144 if (hasConnection()) {
1145 wrappedConnection.flushRequestOutputStream();
1146 } else {
1147 throw new IllegalStateException("Connection has been released");
1148 }
1149 }
1150
1151 public int getSoTimeout() throws SocketException {
1152 if (hasConnection()) {
1153 return wrappedConnection.getSoTimeout();
1154 } else {
1155 throw new IllegalStateException("Connection has been released");
1156 }
1157 }
1158
1159 public String getVirtualHost() {
1160 if (hasConnection()) {
1161 return wrappedConnection.getVirtualHost();
1162 } else {
1163 throw new IllegalStateException("Connection has been released");
1164 }
1165 }
1166
1167 public void setVirtualHost(String host) throws IllegalStateException {
1168 if (hasConnection()) {
1169 wrappedConnection.setVirtualHost(host);
1170 } else {
1171 throw new IllegalStateException("Connection has been released");
1172 }
1173 }
1174
1175 public int getSendBufferSize() throws SocketException {
1176 if (hasConnection()) {
1177 return wrappedConnection.getSendBufferSize();
1178 } else {
1179 throw new IllegalStateException("Connection has been released");
1180 }
1181 }
1182
1183 public void setSendBufferSize(int sendBufferSize) throws SocketException {
1184 if (hasConnection()) {
1185 wrappedConnection.setSendBufferSize(sendBufferSize);
1186 } else {
1187 throw new IllegalStateException("Connection has been released");
1188 }
1189 }
1190
1191 }
1192
1193 }
1194
This page was automatically generated by Maven