• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java $
3  * $Revision: 673450 $
4  * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
5  *
6  * ====================================================================
7  *
8  *  Licensed to the Apache Software Foundation (ASF) under one or more
9  *  contributor license agreements.  See the NOTICE file distributed with
10  *  this work for additional information regarding copyright ownership.
11  *  The ASF licenses this file to You under the Apache License, Version 2.0
12  *  (the "License"); you may not use this file except in compliance with
13  *  the License.  You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  *  Unless required by applicable law or agreed to in writing, software
18  *  distributed under the License is distributed on an "AS IS" BASIS,
19  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  *  See the License for the specific language governing permissions and
21  *  limitations under the License.
22  * ====================================================================
23  *
24  * This software consists of voluntary contributions made by many
25  * individuals on behalf of the Apache Software Foundation.  For more
26  * information on the Apache Software Foundation, please see
27  * <http://www.apache.org/>.
28  *
29  */
30 
31 package org.apache.http.impl.conn.tsccm;
32 
33 import java.io.IOException;
34 import java.lang.ref.Reference;
35 import java.lang.ref.ReferenceQueue;
36 import java.util.Set;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.concurrent.TimeUnit;
40 import java.util.concurrent.locks.Lock;
41 import java.util.concurrent.locks.ReentrantLock;
42 
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45 import org.apache.http.conn.ConnectionPoolTimeoutException;
46 import org.apache.http.conn.OperatedClientConnection;
47 import org.apache.http.conn.routing.HttpRoute;
48 import org.apache.http.impl.conn.IdleConnectionHandler;
49 
50 
51 /**
52  * An abstract connection pool.
53  * It is used by the {@link ThreadSafeClientConnManager}.
54  * The abstract pool includes a {@link #poolLock}, which is used to
55  * synchronize access to the internal pool datastructures.
56  * Don't use <code>synchronized</code> for that purpose!
57  */
58 public abstract class AbstractConnPool implements RefQueueHandler {
59 
60     private final Log log = LogFactory.getLog(getClass());
61 
62     /**
63      * The global lock for this pool.
64      */
65     protected final Lock poolLock;
66 
67 
68     /**
69      * References to issued connections.
70      * Objects in this set are of class
71      * {@link BasicPoolEntryRef BasicPoolEntryRef},
72      * and point to the pool entry for the issued connection.
73      * GCed connections are detected by the missing pool entries.
74      */
75     protected Set<BasicPoolEntryRef> issuedConnections;
76 
77     /** The handler for idle connections. */
78     protected IdleConnectionHandler idleConnHandler;
79 
80     /** The current total number of connections. */
81     protected int numConnections;
82 
83     /**
84      * A reference queue to track loss of pool entries to GC.
85      * The same queue is used to track loss of the connection manager,
86      * so we cannot specialize the type.
87      */
88     protected ReferenceQueue<Object> refQueue;
89 
90     /** A worker (thread) to track loss of pool entries to GC. */
91     private RefQueueWorker refWorker;
92 
93 
94     /** Indicates whether this pool is shut down. */
95     protected volatile boolean isShutDown;
96 
97     /**
98      * Creates a new connection pool.
99      */
AbstractConnPool()100     protected AbstractConnPool() {
101         issuedConnections = new HashSet<BasicPoolEntryRef>();
102         idleConnHandler = new IdleConnectionHandler();
103 
104         boolean fair = false; //@@@ check parameters to decide
105         poolLock = new ReentrantLock(fair);
106     }
107 
108 
109     /**
110      * Enables connection garbage collection (GC).
111      * This method must be called immediately after creating the
112      * connection pool. It is not possible to enable connection GC
113      * after pool entries have been created. Neither is it possible
114      * to disable connection GC.
115      *
116      * @throws IllegalStateException
117      *         if connection GC is already enabled, or if it cannot be
118      *         enabled because there already are pool entries
119      */
enableConnectionGC()120     public void enableConnectionGC()
121         throws IllegalStateException {
122 
123         if (refQueue != null) {
124             throw new IllegalStateException("Connection GC already enabled.");
125         }
126         poolLock.lock();
127         try {
128             if (numConnections > 0) { //@@@ is this check sufficient?
129                 throw new IllegalStateException("Pool already in use.");
130             }
131         } finally {
132             poolLock.unlock();
133         }
134 
135         refQueue  = new ReferenceQueue<Object>();
136         refWorker = new RefQueueWorker(refQueue, this);
137         Thread t = new Thread(refWorker); //@@@ use a thread factory
138         t.setDaemon(true);
139         t.setName("RefQueueWorker@" + this);
140         t.start();
141     }
142 
143 
144     /**
145      * Obtains a pool entry with a connection within the given timeout.
146      *
147      * @param route     the route for which to get the connection
148      * @param timeout   the timeout, 0 or negative for no timeout
149      * @param tunit     the unit for the <code>timeout</code>,
150      *                  may be <code>null</code> only if there is no timeout
151      *
152      * @return  pool entry holding a connection for the route
153      *
154      * @throws ConnectionPoolTimeoutException
155      *         if the timeout expired
156      * @throws InterruptedException
157      *         if the calling thread was interrupted
158      */
159     public final
getEntry( HttpRoute route, Object state, long timeout, TimeUnit tunit)160         BasicPoolEntry getEntry(
161                 HttpRoute route,
162                 Object state,
163                 long timeout,
164                 TimeUnit tunit)
165                     throws ConnectionPoolTimeoutException, InterruptedException {
166         return requestPoolEntry(route, state).getPoolEntry(timeout, tunit);
167     }
168 
169     /**
170      * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry}
171      * can be obtained, or the request can be aborted.
172      */
requestPoolEntry(HttpRoute route, Object state)173     public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state);
174 
175 
176     /**
177      * Returns an entry into the pool.
178      * The connection of the entry is expected to be in a suitable state,
179      * either open and re-usable, or closed. The pool will not make any
180      * attempt to determine whether it can be re-used or not.
181      *
182      * @param entry     the entry for the connection to release
183      * @param reusable  <code>true</code> if the entry is deemed
184      *                  reusable, <code>false</code> otherwise.
185      * @param validDuration The duration that the entry should remain free and reusable.
186      * @param timeUnit The unit of time the duration is measured in.
187      */
freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit)188     public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit)
189         ;
190 
191 
192 
193     // non-javadoc, see interface RefQueueHandler
194 // BEGIN android-changed
handleReference(Reference ref)195     public void handleReference(Reference ref) {
196 // END android-changed
197         poolLock.lock();
198         try {
199 
200             if (ref instanceof BasicPoolEntryRef) {
201                 // check if the GCed pool entry was still in use
202                 //@@@ find a way to detect this without lookup
203                 //@@@ flag in the BasicPoolEntryRef, to be reset when freed?
204                 final boolean lost = issuedConnections.remove(ref);
205                 if (lost) {
206                     final HttpRoute route =
207                         ((BasicPoolEntryRef)ref).getRoute();
208                     if (log.isDebugEnabled()) {
209                         log.debug("Connection garbage collected. " + route);
210                     }
211                     handleLostEntry(route);
212                 }
213             }
214 
215         } finally {
216             poolLock.unlock();
217         }
218     }
219 
220 
221     /**
222      * Handles cleaning up for a lost pool entry with the given route.
223      * A lost pool entry corresponds to a connection that was
224      * garbage collected instead of being properly released.
225      *
226      * @param route     the route of the pool entry that was lost
227      */
handleLostEntry(HttpRoute route)228     protected abstract void handleLostEntry(HttpRoute route)
229         ;
230 
231 
232     /**
233      * Closes idle connections.
234      *
235      * @param idletime  the time the connections should have been idle
236      *                  in order to be closed now
237      * @param tunit     the unit for the <code>idletime</code>
238      */
closeIdleConnections(long idletime, TimeUnit tunit)239     public void closeIdleConnections(long idletime, TimeUnit tunit) {
240 
241         // idletime can be 0 or negative, no problem there
242         if (tunit == null) {
243             throw new IllegalArgumentException("Time unit must not be null.");
244         }
245 
246         poolLock.lock();
247         try {
248             idleConnHandler.closeIdleConnections(tunit.toMillis(idletime));
249         } finally {
250             poolLock.unlock();
251         }
252     }
253 
closeExpiredConnections()254     public void closeExpiredConnections() {
255         poolLock.lock();
256         try {
257             idleConnHandler.closeExpiredConnections();
258         } finally {
259             poolLock.unlock();
260         }
261     }
262 
263 
264     //@@@ revise this cleanup stuff (closeIdle+deleteClosed), it's not good
265 
266     /**
267      * Deletes all entries for closed connections.
268      */
deleteClosedConnections()269     public abstract void deleteClosedConnections()
270         ;
271 
272 
273     /**
274      * Shuts down this pool and all associated resources.
275      * Overriding methods MUST call the implementation here!
276      */
shutdown()277     public void shutdown() {
278 
279         poolLock.lock();
280         try {
281 
282             if (isShutDown)
283                 return;
284 
285             // no point in monitoring GC anymore
286             if (refWorker != null)
287                 refWorker.shutdown();
288 
289             // close all connections that are issued to an application
290             Iterator<BasicPoolEntryRef> iter = issuedConnections.iterator();
291             while (iter.hasNext()) {
292                 BasicPoolEntryRef per = iter.next();
293                 iter.remove();
294                 BasicPoolEntry entry = per.get();
295                 if (entry != null) {
296                     closeConnection(entry.getConnection());
297                 }
298             }
299 
300             // remove all references to connections
301             //@@@ use this for shutting them down instead?
302             idleConnHandler.removeAll();
303 
304             isShutDown = true;
305 
306         } finally {
307             poolLock.unlock();
308         }
309     }
310 
311 
312     /**
313      * Closes a connection from this pool.
314      *
315      * @param conn      the connection to close, or <code>null</code>
316      */
closeConnection(final OperatedClientConnection conn)317     protected void closeConnection(final OperatedClientConnection conn) {
318         if (conn != null) {
319             try {
320                 conn.close();
321             } catch (IOException ex) {
322                 log.debug("I/O error closing connection", ex);
323             }
324         }
325     }
326 
327 
328 
329 
330 
331 } // class AbstractConnPool
332 
333