View Javadoc

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