View Javadoc

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