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