1 /*
2 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v 1.17.2.2 2003/07/31 02:31:09 mbecke Exp $
3 * $Revision: 1.17.2.2 $
4 * $Date: 2003/07/31 02:31:09 $
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 HostConnectionPool hostPool = connectionPool.getHostPool(config);
713 hostPool.numConnections--;
714
715 connectionPool.numConnections--;
716 connectionPool.notifyWaitingThread(config);
717 }
718 }
719 }
720
721 /***
722 * Start execution.
723 */
724 public void run() {
725 while (true) {
726 try {
727 Reference ref = referenceQueue.remove();
728 if (ref != null) {
729 handleReference(ref);
730 }
731 } catch (InterruptedException e) {
732 LOG.debug("ReferenceQueueThread interrupted", e);
733 }
734 }
735 }
736
737 }
738
739 /***
740 * An HttpConnection wrapper that ensures a connection cannot be used
741 * once released.
742 */
743 private static class HttpConnectionAdapter extends HttpConnection {
744
745 // the wrapped connection
746 private HttpConnection wrappedConnection;
747
748 /***
749 * Creates a new HttpConnectionAdapter.
750 * @param connection the connection to be wrapped
751 */
752 public HttpConnectionAdapter(HttpConnection connection) {
753 super(connection.getHost(), connection.getPort(), connection.getProtocol());
754 this.wrappedConnection = connection;
755 }
756
757 /***
758 * Tests if the wrapped connection is still available.
759 * @return boolean
760 */
761 protected boolean hasConnection() {
762 return wrappedConnection != null;
763 }
764
765 /***
766 * @return HttpConnection
767 */
768 HttpConnection getWrappedConnection() {
769 return wrappedConnection;
770 }
771
772 public void close() {
773 if (hasConnection()) {
774 wrappedConnection.close();
775 } else {
776 // do nothing
777 }
778 }
779
780 public InetAddress getLocalAddress() {
781 if (hasConnection()) {
782 return wrappedConnection.getLocalAddress();
783 } else {
784 return null;
785 }
786 }
787
788 public boolean isStaleCheckingEnabled() {
789 if (hasConnection()) {
790 return wrappedConnection.isStaleCheckingEnabled();
791 } else {
792 return false;
793 }
794 }
795
796 public void setLocalAddress(InetAddress localAddress) {
797 if (hasConnection()) {
798 wrappedConnection.setLocalAddress(localAddress);
799 } else {
800 throw new IllegalStateException("Connection has been released");
801 }
802 }
803
804 public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
805 if (hasConnection()) {
806 wrappedConnection.setStaleCheckingEnabled(staleCheckEnabled);
807 } else {
808 throw new IllegalStateException("Connection has been released");
809 }
810 }
811
812 public String getHost() {
813 if (hasConnection()) {
814 return wrappedConnection.getHost();
815 } else {
816 return null;
817 }
818 }
819
820 public HttpConnectionManager getHttpConnectionManager() {
821 if (hasConnection()) {
822 return wrappedConnection.getHttpConnectionManager();
823 } else {
824 return null;
825 }
826 }
827
828 public InputStream getLastResponseInputStream() {
829 if (hasConnection()) {
830 return wrappedConnection.getLastResponseInputStream();
831 } else {
832 return null;
833 }
834 }
835
836 public int getPort() {
837 if (hasConnection()) {
838 return wrappedConnection.getPort();
839 } else {
840 return -1;
841 }
842 }
843
844 public Protocol getProtocol() {
845 if (hasConnection()) {
846 return wrappedConnection.getProtocol();
847 } else {
848 return null;
849 }
850 }
851
852 public String getProxyHost() {
853 if (hasConnection()) {
854 return wrappedConnection.getProxyHost();
855 } else {
856 return null;
857 }
858 }
859
860 public int getProxyPort() {
861 if (hasConnection()) {
862 return wrappedConnection.getProxyPort();
863 } else {
864 return -1;
865 }
866 }
867
868 public OutputStream getRequestOutputStream()
869 throws IOException, IllegalStateException {
870 if (hasConnection()) {
871 return wrappedConnection.getRequestOutputStream();
872 } else {
873 return null;
874 }
875 }
876
877 public OutputStream getRequestOutputStream(boolean useChunking)
878 throws IOException, IllegalStateException {
879 if (hasConnection()) {
880 return wrappedConnection.getRequestOutputStream(useChunking);
881 } else {
882 return null;
883 }
884 }
885
886 public InputStream getResponseInputStream()
887 throws IOException, IllegalStateException {
888 if (hasConnection()) {
889 return wrappedConnection.getResponseInputStream();
890 } else {
891 return null;
892 }
893 }
894
895 public InputStream getResponseInputStream(HttpMethod method)
896 throws IOException, IllegalStateException {
897 if (hasConnection()) {
898 return wrappedConnection.getResponseInputStream(method);
899 } else {
900 return null;
901 }
902 }
903
904 public boolean isOpen() {
905 if (hasConnection()) {
906 return wrappedConnection.isOpen();
907 } else {
908 return false;
909 }
910 }
911
912 public boolean isProxied() {
913 if (hasConnection()) {
914 return wrappedConnection.isProxied();
915 } else {
916 return false;
917 }
918 }
919
920 public boolean isResponseAvailable() throws IOException {
921 if (hasConnection()) {
922 return wrappedConnection.isResponseAvailable();
923 } else {
924 return false;
925 }
926 }
927
928 public boolean isResponseAvailable(int timeout) throws IOException {
929 if (hasConnection()) {
930 return wrappedConnection.isResponseAvailable(timeout);
931 } else {
932 return false;
933 }
934 }
935
936 public boolean isSecure() {
937 if (hasConnection()) {
938 return wrappedConnection.isSecure();
939 } else {
940 return false;
941 }
942 }
943
944 public boolean isTransparent() {
945 if (hasConnection()) {
946 return wrappedConnection.isTransparent();
947 } else {
948 return false;
949 }
950 }
951
952 public void open() throws IOException {
953 if (hasConnection()) {
954 wrappedConnection.open();
955 } else {
956 throw new IllegalStateException("Connection has been released");
957 }
958 }
959
960 public void print(String data)
961 throws IOException, IllegalStateException, HttpRecoverableException {
962 if (hasConnection()) {
963 wrappedConnection.print(data);
964 } else {
965 throw new IllegalStateException("Connection has been released");
966 }
967 }
968
969 public void printLine()
970 throws IOException, IllegalStateException, HttpRecoverableException {
971 if (hasConnection()) {
972 wrappedConnection.printLine();
973 } else {
974 throw new IllegalStateException("Connection has been released");
975 }
976 }
977
978 public void printLine(String data)
979 throws IOException, IllegalStateException, HttpRecoverableException {
980 if (hasConnection()) {
981 wrappedConnection.printLine(data);
982 } else {
983 throw new IllegalStateException("Connection has been released");
984 }
985 }
986
987 public String readLine() throws IOException, IllegalStateException {
988 if (hasConnection()) {
989 return wrappedConnection.readLine();
990 } else {
991 throw new IllegalStateException("Connection has been released");
992 }
993 }
994
995 public void releaseConnection() {
996 if (hasConnection()) {
997 HttpConnection wrappedConnection = this.wrappedConnection;
998 this.wrappedConnection = null;
999 wrappedConnection.releaseConnection();
1000 } else {
1001 // do nothing
1002 }
1003 }
1004
1005 public void setConnectionTimeout(int timeout) {
1006 if (hasConnection()) {
1007 wrappedConnection.setConnectionTimeout(timeout);
1008 } else {
1009 // do nothing
1010 }
1011 }
1012
1013 public void setHost(String host) throws IllegalStateException {
1014 if (hasConnection()) {
1015 wrappedConnection.setHost(host);
1016 } else {
1017 // do nothing
1018 }
1019 }
1020
1021 public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1022 if (hasConnection()) {
1023 wrappedConnection.setHttpConnectionManager(httpConnectionManager);
1024 } else {
1025 // do nothing
1026 }
1027 }
1028
1029 public void setLastResponseInputStream(InputStream inStream) {
1030 if (hasConnection()) {
1031 wrappedConnection.setLastResponseInputStream(inStream);
1032 } else {
1033 // do nothing
1034 }
1035 }
1036
1037 public void setPort(int port) throws IllegalStateException {
1038 if (hasConnection()) {
1039 wrappedConnection.setPort(port);
1040 } else {
1041 // do nothing
1042 }
1043 }
1044
1045 public void setProtocol(Protocol protocol) {
1046 if (hasConnection()) {
1047 wrappedConnection.setProtocol(protocol);
1048 } else {
1049 // do nothing
1050 }
1051 }
1052
1053 public void setProxyHost(String host) throws IllegalStateException {
1054 if (hasConnection()) {
1055 wrappedConnection.setProxyHost(host);
1056 } else {
1057 // do nothing
1058 }
1059 }
1060
1061 public void setProxyPort(int port) throws IllegalStateException {
1062 if (hasConnection()) {
1063 wrappedConnection.setProxyPort(port);
1064 } else {
1065 // do nothing
1066 }
1067 }
1068
1069 public void setSecure(boolean secure) throws IllegalStateException {
1070 if (hasConnection()) {
1071 wrappedConnection.setSecure(secure);
1072 } else {
1073 // do nothing
1074 }
1075 }
1076
1077 public void setSoTimeout(int timeout)
1078 throws SocketException, IllegalStateException {
1079 if (hasConnection()) {
1080 wrappedConnection.setSoTimeout(timeout);
1081 } else {
1082 // do nothing
1083 }
1084 }
1085
1086 public void shutdownOutput() {
1087 if (hasConnection()) {
1088 wrappedConnection.shutdownOutput();
1089 } else {
1090 // do nothing
1091 }
1092 }
1093
1094 public void tunnelCreated() throws IllegalStateException, IOException {
1095 if (hasConnection()) {
1096 wrappedConnection.tunnelCreated();
1097 } else {
1098 // do nothing
1099 }
1100 }
1101
1102 public void write(byte[] data, int offset, int length)
1103 throws IOException, IllegalStateException, HttpRecoverableException {
1104 if (hasConnection()) {
1105 wrappedConnection.write(data, offset, length);
1106 } else {
1107 throw new IllegalStateException("Connection has been released");
1108 }
1109 }
1110
1111 public void write(byte[] data)
1112 throws IOException, IllegalStateException, HttpRecoverableException {
1113 if (hasConnection()) {
1114 wrappedConnection.write(data);
1115 } else {
1116 throw new IllegalStateException("Connection has been released");
1117 }
1118 }
1119
1120 public void writeLine()
1121 throws IOException, IllegalStateException, HttpRecoverableException {
1122 if (hasConnection()) {
1123 wrappedConnection.writeLine();
1124 } else {
1125 throw new IllegalStateException("Connection has been released");
1126 }
1127 }
1128
1129 public void writeLine(byte[] data)
1130 throws IOException, IllegalStateException, HttpRecoverableException {
1131 if (hasConnection()) {
1132 wrappedConnection.writeLine(data);
1133 } else {
1134 throw new IllegalStateException("Connection has been released");
1135 }
1136 }
1137
1138 public void flushRequestOutputStream() throws IOException {
1139 if (hasConnection()) {
1140 wrappedConnection.flushRequestOutputStream();
1141 } else {
1142 throw new IllegalStateException("Connection has been released");
1143 }
1144 }
1145
1146 public int getSoTimeout() throws SocketException {
1147 if (hasConnection()) {
1148 return wrappedConnection.getSoTimeout();
1149 } else {
1150 throw new IllegalStateException("Connection has been released");
1151 }
1152 }
1153
1154 public String getVirtualHost() {
1155 if (hasConnection()) {
1156 return wrappedConnection.getVirtualHost();
1157 } else {
1158 throw new IllegalStateException("Connection has been released");
1159 }
1160 }
1161
1162 public void setVirtualHost(String host) throws IllegalStateException {
1163 if (hasConnection()) {
1164 wrappedConnection.setVirtualHost(host);
1165 } else {
1166 throw new IllegalStateException("Connection has been released");
1167 }
1168 }
1169
1170 public int getSendBufferSize() throws SocketException {
1171 if (hasConnection()) {
1172 return wrappedConnection.getSendBufferSize();
1173 } else {
1174 throw new IllegalStateException("Connection has been released");
1175 }
1176 }
1177
1178 public void setSendBufferSize(int sendBufferSize) throws SocketException {
1179 if (hasConnection()) {
1180 wrappedConnection.setSendBufferSize(sendBufferSize);
1181 } else {
1182 throw new IllegalStateException("Connection has been released");
1183 }
1184 }
1185
1186 }
1187
1188 }
1189
This page was automatically generated by Maven