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