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