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