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
31package org.apache.http.impl.conn.tsccm;
32
33import java.io.IOException;
34import java.lang.ref.Reference;
35import java.lang.ref.ReferenceQueue;
36import java.util.Set;
37import java.util.HashSet;
38import java.util.Iterator;
39import java.util.concurrent.TimeUnit;
40import java.util.concurrent.locks.Lock;
41import java.util.concurrent.locks.ReentrantLock;
42
43import org.apache.commons.logging.Log;
44import org.apache.commons.logging.LogFactory;
45import org.apache.http.conn.ConnectionPoolTimeoutException;
46import org.apache.http.conn.OperatedClientConnection;
47import org.apache.http.conn.routing.HttpRoute;
48import 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 * @deprecated Please use {@link java.net.URL#openConnection} instead.
59 *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
60 *     for further details.
61 */
62@Deprecated
63public abstract class AbstractConnPool implements RefQueueHandler {
64
65    private final Log log = LogFactory.getLog(getClass());
66
67    /**
68     * The global lock for this pool.
69     */
70    protected final Lock poolLock;
71
72
73    /**
74     * References to issued connections.
75     * Objects in this set are of class
76     * {@link BasicPoolEntryRef BasicPoolEntryRef},
77     * and point to the pool entry for the issued connection.
78     * GCed connections are detected by the missing pool entries.
79     */
80    protected Set<BasicPoolEntryRef> issuedConnections;
81
82    /** The handler for idle connections. */
83    protected IdleConnectionHandler idleConnHandler;
84
85    /** The current total number of connections. */
86    protected int numConnections;
87
88    /**
89     * A reference queue to track loss of pool entries to GC.
90     * The same queue is used to track loss of the connection manager,
91     * so we cannot specialize the type.
92     */
93    protected ReferenceQueue<Object> refQueue;
94
95    /** A worker (thread) to track loss of pool entries to GC. */
96    private RefQueueWorker refWorker;
97
98
99    /** Indicates whether this pool is shut down. */
100    protected volatile boolean isShutDown;
101
102    /**
103     * Creates a new connection pool.
104     */
105    protected AbstractConnPool() {
106        issuedConnections = new HashSet<BasicPoolEntryRef>();
107        idleConnHandler = new IdleConnectionHandler();
108
109        boolean fair = false; //@@@ check parameters to decide
110        poolLock = new ReentrantLock(fair);
111    }
112
113
114    /**
115     * Enables connection garbage collection (GC).
116     * This method must be called immediately after creating the
117     * connection pool. It is not possible to enable connection GC
118     * after pool entries have been created. Neither is it possible
119     * to disable connection GC.
120     *
121     * @throws IllegalStateException
122     *         if connection GC is already enabled, or if it cannot be
123     *         enabled because there already are pool entries
124     */
125    public void enableConnectionGC()
126        throws IllegalStateException {
127
128        if (refQueue != null) {
129            throw new IllegalStateException("Connection GC already enabled.");
130        }
131        poolLock.lock();
132        try {
133            if (numConnections > 0) { //@@@ is this check sufficient?
134                throw new IllegalStateException("Pool already in use.");
135            }
136        } finally {
137            poolLock.unlock();
138        }
139
140        refQueue  = new ReferenceQueue<Object>();
141        refWorker = new RefQueueWorker(refQueue, this);
142        Thread t = new Thread(refWorker); //@@@ use a thread factory
143        t.setDaemon(true);
144        t.setName("RefQueueWorker@" + this);
145        t.start();
146    }
147
148
149    /**
150     * Obtains a pool entry with a connection within the given timeout.
151     *
152     * @param route     the route for which to get the connection
153     * @param timeout   the timeout, 0 or negative for no timeout
154     * @param tunit     the unit for the <code>timeout</code>,
155     *                  may be <code>null</code> only if there is no timeout
156     *
157     * @return  pool entry holding a connection for the route
158     *
159     * @throws ConnectionPoolTimeoutException
160     *         if the timeout expired
161     * @throws InterruptedException
162     *         if the calling thread was interrupted
163     */
164    public final
165        BasicPoolEntry getEntry(
166                HttpRoute route,
167                Object state,
168                long timeout,
169                TimeUnit tunit)
170                    throws ConnectionPoolTimeoutException, InterruptedException {
171        return requestPoolEntry(route, state).getPoolEntry(timeout, tunit);
172    }
173
174    /**
175     * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry}
176     * can be obtained, or the request can be aborted.
177     */
178    public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state);
179
180
181    /**
182     * Returns an entry into the pool.
183     * The connection of the entry is expected to be in a suitable state,
184     * either open and re-usable, or closed. The pool will not make any
185     * attempt to determine whether it can be re-used or not.
186     *
187     * @param entry     the entry for the connection to release
188     * @param reusable  <code>true</code> if the entry is deemed
189     *                  reusable, <code>false</code> otherwise.
190     * @param validDuration The duration that the entry should remain free and reusable.
191     * @param timeUnit The unit of time the duration is measured in.
192     */
193    public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit)
194        ;
195
196
197
198    // non-javadoc, see interface RefQueueHandler
199// BEGIN android-changed
200    public void handleReference(Reference ref) {
201// END android-changed
202        poolLock.lock();
203        try {
204
205            if (ref instanceof BasicPoolEntryRef) {
206                // check if the GCed pool entry was still in use
207                //@@@ find a way to detect this without lookup
208                //@@@ flag in the BasicPoolEntryRef, to be reset when freed?
209                final boolean lost = issuedConnections.remove(ref);
210                if (lost) {
211                    final HttpRoute route =
212                        ((BasicPoolEntryRef)ref).getRoute();
213                    if (log.isDebugEnabled()) {
214                        log.debug("Connection garbage collected. " + route);
215                    }
216                    handleLostEntry(route);
217                }
218            }
219
220        } finally {
221            poolLock.unlock();
222        }
223    }
224
225
226    /**
227     * Handles cleaning up for a lost pool entry with the given route.
228     * A lost pool entry corresponds to a connection that was
229     * garbage collected instead of being properly released.
230     *
231     * @param route     the route of the pool entry that was lost
232     */
233    protected abstract void handleLostEntry(HttpRoute route)
234        ;
235
236
237    /**
238     * Closes idle connections.
239     *
240     * @param idletime  the time the connections should have been idle
241     *                  in order to be closed now
242     * @param tunit     the unit for the <code>idletime</code>
243     */
244    public void closeIdleConnections(long idletime, TimeUnit tunit) {
245
246        // idletime can be 0 or negative, no problem there
247        if (tunit == null) {
248            throw new IllegalArgumentException("Time unit must not be null.");
249        }
250
251        poolLock.lock();
252        try {
253            idleConnHandler.closeIdleConnections(tunit.toMillis(idletime));
254        } finally {
255            poolLock.unlock();
256        }
257    }
258
259    public void closeExpiredConnections() {
260        poolLock.lock();
261        try {
262            idleConnHandler.closeExpiredConnections();
263        } finally {
264            poolLock.unlock();
265        }
266    }
267
268
269    //@@@ revise this cleanup stuff (closeIdle+deleteClosed), it's not good
270
271    /**
272     * Deletes all entries for closed connections.
273     */
274    public abstract void deleteClosedConnections()
275        ;
276
277
278    /**
279     * Shuts down this pool and all associated resources.
280     * Overriding methods MUST call the implementation here!
281     */
282    public void shutdown() {
283
284        poolLock.lock();
285        try {
286
287            if (isShutDown)
288                return;
289
290            // no point in monitoring GC anymore
291            if (refWorker != null)
292                refWorker.shutdown();
293
294            // close all connections that are issued to an application
295            Iterator<BasicPoolEntryRef> iter = issuedConnections.iterator();
296            while (iter.hasNext()) {
297                BasicPoolEntryRef per = iter.next();
298                iter.remove();
299                BasicPoolEntry entry = per.get();
300                if (entry != null) {
301                    closeConnection(entry.getConnection());
302                }
303            }
304
305            // remove all references to connections
306            //@@@ use this for shutting them down instead?
307            idleConnHandler.removeAll();
308
309            isShutDown = true;
310
311        } finally {
312            poolLock.unlock();
313        }
314    }
315
316
317    /**
318     * Closes a connection from this pool.
319     *
320     * @param conn      the connection to close, or <code>null</code>
321     */
322    protected void closeConnection(final OperatedClientConnection conn) {
323        if (conn != null) {
324            try {
325                conn.close();
326            } catch (IOException ex) {
327                log.debug("I/O error closing connection", ex);
328            }
329        }
330    }
331
332
333
334
335
336} // class AbstractConnPool
337
338