summaryrefslogtreecommitdiffstats
path: root/src/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
blob: 2b37d7293a347c9b095c05831a548af3f6cda137 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/*
 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java $
 * $Revision: 673450 $
 * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
 *
 * ====================================================================
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.http.impl.conn.tsccm;

import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.OperatedClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.conn.IdleConnectionHandler;


/**
 * An abstract connection pool.
 * It is used by the {@link ThreadSafeClientConnManager}.
 * The abstract pool includes a {@link #poolLock}, which is used to
 * synchronize access to the internal pool datastructures.
 * Don't use <code>synchronized</code> for that purpose!
 */
public abstract class AbstractConnPool implements RefQueueHandler {

    private final Log log = LogFactory.getLog(getClass());

    /**
     * The global lock for this pool.
     */
    protected final Lock poolLock;


    /**
     * References to issued connections.
     * Objects in this set are of class
     * {@link BasicPoolEntryRef BasicPoolEntryRef},
     * and point to the pool entry for the issued connection.
     * GCed connections are detected by the missing pool entries.
     */
    protected Set<BasicPoolEntryRef> issuedConnections;

    /** The handler for idle connections. */
    protected IdleConnectionHandler idleConnHandler;

    /** The current total number of connections. */
    protected int numConnections;

    /**
     * A reference queue to track loss of pool entries to GC.
     * The same queue is used to track loss of the connection manager,
     * so we cannot specialize the type.
     */
    protected ReferenceQueue<Object> refQueue;

    /** A worker (thread) to track loss of pool entries to GC. */
    private RefQueueWorker refWorker;


    /** Indicates whether this pool is shut down. */
    protected volatile boolean isShutDown;

    /**
     * Creates a new connection pool.
     */
    protected AbstractConnPool() {
        issuedConnections = new HashSet<BasicPoolEntryRef>();
        idleConnHandler = new IdleConnectionHandler();

        boolean fair = false; //@@@ check parameters to decide
        poolLock = new ReentrantLock(fair);
    }


    /**
     * Enables connection garbage collection (GC).
     * This method must be called immediately after creating the
     * connection pool. It is not possible to enable connection GC
     * after pool entries have been created. Neither is it possible
     * to disable connection GC.
     *
     * @throws IllegalStateException
     *         if connection GC is already enabled, or if it cannot be
     *         enabled because there already are pool entries
     */
    public void enableConnectionGC()
        throws IllegalStateException {

        if (refQueue != null) {
            throw new IllegalStateException("Connection GC already enabled.");
        }
        poolLock.lock();
        try {
            if (numConnections > 0) { //@@@ is this check sufficient?
                throw new IllegalStateException("Pool already in use.");
            }
        } finally {
            poolLock.unlock();
        }

        refQueue  = new ReferenceQueue<Object>();
        refWorker = new RefQueueWorker(refQueue, this);
        Thread t = new Thread(refWorker); //@@@ use a thread factory
        t.setDaemon(true);
        t.setName("RefQueueWorker@" + this);
        t.start();
    }


    /**
     * Obtains a pool entry with a connection within the given timeout.
     *
     * @param route     the route for which to get the connection
     * @param timeout   the timeout, 0 or negative for no timeout
     * @param tunit     the unit for the <code>timeout</code>,
     *                  may be <code>null</code> only if there is no timeout
     *
     * @return  pool entry holding a connection for the route
     *
     * @throws ConnectionPoolTimeoutException
     *         if the timeout expired
     * @throws InterruptedException
     *         if the calling thread was interrupted
     */
    public final
        BasicPoolEntry getEntry(
                HttpRoute route, 
                Object state,
                long timeout, 
                TimeUnit tunit)
                    throws ConnectionPoolTimeoutException, InterruptedException {
        return requestPoolEntry(route, state).getPoolEntry(timeout, tunit);
    }
    
    /**
     * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry}
     * can be obtained, or the request can be aborted.
     */
    public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state);


    /**
     * Returns an entry into the pool.
     * The connection of the entry is expected to be in a suitable state,
     * either open and re-usable, or closed. The pool will not make any
     * attempt to determine whether it can be re-used or not.
     *
     * @param entry     the entry for the connection to release
     * @param reusable  <code>true</code> if the entry is deemed 
     *                  reusable, <code>false</code> otherwise.
     * @param validDuration The duration that the entry should remain free and reusable.
     * @param timeUnit The unit of time the duration is measured in.
     */
    public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit)
        ;



    // non-javadoc, see interface RefQueueHandler
// BEGIN android-changed
    public void handleReference(Reference ref) {
// END android-changed
        poolLock.lock();
        try {

            if (ref instanceof BasicPoolEntryRef) {
                // check if the GCed pool entry was still in use
                //@@@ find a way to detect this without lookup
                //@@@ flag in the BasicPoolEntryRef, to be reset when freed?
                final boolean lost = issuedConnections.remove(ref);
                if (lost) {
                    final HttpRoute route =
                        ((BasicPoolEntryRef)ref).getRoute();
                    if (log.isDebugEnabled()) {
                        log.debug("Connection garbage collected. " + route);
                    }
                    handleLostEntry(route);
                }
            }

        } finally {
            poolLock.unlock();
        }
    }


    /**
     * Handles cleaning up for a lost pool entry with the given route.
     * A lost pool entry corresponds to a connection that was
     * garbage collected instead of being properly released.
     *
     * @param route     the route of the pool entry that was lost
     */
    protected abstract void handleLostEntry(HttpRoute route)
        ;


    /**
     * Closes idle connections.
     *
     * @param idletime  the time the connections should have been idle
     *                  in order to be closed now
     * @param tunit     the unit for the <code>idletime</code>
     */
    public void closeIdleConnections(long idletime, TimeUnit tunit) {

        // idletime can be 0 or negative, no problem there
        if (tunit == null) {
            throw new IllegalArgumentException("Time unit must not be null.");
        }

        poolLock.lock();
        try {
            idleConnHandler.closeIdleConnections(tunit.toMillis(idletime));
        } finally {
            poolLock.unlock();
        }
    }
    
    public void closeExpiredConnections() {
        poolLock.lock();
        try {
            idleConnHandler.closeExpiredConnections();
        } finally {
            poolLock.unlock();
        }
    }

        
    //@@@ revise this cleanup stuff (closeIdle+deleteClosed), it's not good

    /**
     * Deletes all entries for closed connections.
     */
    public abstract void deleteClosedConnections()
        ;


    /**
     * Shuts down this pool and all associated resources.
     * Overriding methods MUST call the implementation here!
     */
    public void shutdown() {

        poolLock.lock();
        try {

            if (isShutDown)
                return;

            // no point in monitoring GC anymore
            if (refWorker != null)
                refWorker.shutdown();

            // close all connections that are issued to an application
            Iterator<BasicPoolEntryRef> iter = issuedConnections.iterator();
            while (iter.hasNext()) {
                BasicPoolEntryRef per = iter.next();
                iter.remove();
                BasicPoolEntry entry = per.get();
                if (entry != null) {
                    closeConnection(entry.getConnection());
                }
            }

            // remove all references to connections
            //@@@ use this for shutting them down instead?
            idleConnHandler.removeAll();

            isShutDown = true;

        } finally {
            poolLock.unlock();
        }
    }


    /**
     * Closes a connection from this pool.
     *
     * @param conn      the connection to close, or <code>null</code>
     */
    protected void closeConnection(final OperatedClientConnection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (IOException ex) {
                log.debug("I/O error closing connection", ex);
            }
        }
    }





} // class AbstractConnPool