View Javadoc
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