1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java $
3 * $Revision: 673450 $
4 * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with 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,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http.impl.conn;
33
34import dalvik.system.SocketTagger;
35import java.io.IOException;
36import java.net.Socket;
37import java.util.concurrent.TimeUnit;
38
39import org.apache.commons.logging.Log;
40import org.apache.commons.logging.LogFactory;
41import org.apache.http.conn.ClientConnectionManager;
42import org.apache.http.conn.ClientConnectionOperator;
43import org.apache.http.conn.ClientConnectionRequest;
44import org.apache.http.conn.ManagedClientConnection;
45import org.apache.http.conn.routing.HttpRoute;
46import org.apache.http.conn.routing.RouteTracker;
47import org.apache.http.conn.scheme.SchemeRegistry;
48import org.apache.http.params.HttpParams;
49
50
51/**
52 * A connection "manager" for a single connection.
53 * This manager is good only for single-threaded use.
54 * Allocation <i>always</i> returns the connection immediately,
55 * even if it has not been released after the previous allocation.
56 * In that case, a {@link #MISUSE_MESSAGE warning} is logged
57 * and the previously issued connection is revoked.
58 * <p>
59 * This class is derived from <code>SimpleHttpConnectionManager</code>
60 * in HttpClient 3. See there for original authors.
61 * </p>
62 *
63 * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
64 * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
65 *
66 *
67 * <!-- empty lines to avoid svn diff problems -->
68 * @version   $Revision: 673450 $
69 *
70 * @since 4.0
71 *
72 * @deprecated Please use {@link java.net.URL#openConnection} instead.
73 *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
74 *     for further details.
75 */
76@Deprecated
77public class SingleClientConnManager implements ClientConnectionManager {
78
79    private final Log log = LogFactory.getLog(getClass());
80
81    /** The message to be logged on multiple allocation. */
82    public final static String MISUSE_MESSAGE =
83    "Invalid use of SingleClientConnManager: connection still allocated.\n" +
84    "Make sure to release the connection before allocating another one.";
85
86
87    /** The schemes supported by this connection manager. */
88    protected SchemeRegistry schemeRegistry;
89
90    /** The operator for opening and updating connections. */
91    protected ClientConnectionOperator connOperator;
92
93    /** The one and only entry in this pool. */
94    protected PoolEntry uniquePoolEntry;
95
96    /** The currently issued managed connection, if any. */
97    protected ConnAdapter managedConn;
98
99    /** The time of the last connection release, or -1. */
100    protected long lastReleaseTime;
101
102    /** The time the last released connection expires and shouldn't be reused. */
103    protected long connectionExpiresTime;
104
105    /** Whether the connection should be shut down  on release. */
106    protected boolean alwaysShutDown;
107
108    /** Indicates whether this connection manager is shut down. */
109    protected volatile boolean isShutDown;
110
111
112
113
114    /**
115     * Creates a new simple connection manager.
116     *
117     * @param params    the parameters for this manager
118     * @param schreg    the scheme registry
119     */
120    public SingleClientConnManager(HttpParams params,
121                                   SchemeRegistry schreg) {
122
123        if (schreg == null) {
124            throw new IllegalArgumentException
125                ("Scheme registry must not be null.");
126        }
127        this.schemeRegistry  = schreg;
128        this.connOperator    = createConnectionOperator(schreg);
129        this.uniquePoolEntry = new PoolEntry();
130        this.managedConn     = null;
131        this.lastReleaseTime = -1L;
132        this.alwaysShutDown  = false; //@@@ from params? as argument?
133        this.isShutDown      = false;
134
135    } // <constructor>
136
137
138    @Override
139    protected void finalize() throws Throwable {
140        shutdown();
141        super.finalize();
142    }
143
144
145    // non-javadoc, see interface ClientConnectionManager
146    public SchemeRegistry getSchemeRegistry() {
147        return this.schemeRegistry;
148    }
149
150
151    /**
152     * Hook for creating the connection operator.
153     * It is called by the constructor.
154     * Derived classes can override this method to change the
155     * instantiation of the operator.
156     * The default implementation here instantiates
157     * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
158     *
159     * @param schreg    the scheme registry to use, or <code>null</code>
160     *
161     * @return  the connection operator to use
162     */
163    protected ClientConnectionOperator
164        createConnectionOperator(SchemeRegistry schreg) {
165
166        return new DefaultClientConnectionOperator(schreg);
167    }
168
169
170    /**
171     * Asserts that this manager is not shut down.
172     *
173     * @throws IllegalStateException    if this manager is shut down
174     */
175    protected final void assertStillUp()
176        throws IllegalStateException {
177
178        if (this.isShutDown)
179            throw new IllegalStateException("Manager is shut down.");
180    }
181
182
183    public final ClientConnectionRequest requestConnection(
184            final HttpRoute route,
185            final Object state) {
186
187        return new ClientConnectionRequest() {
188
189            public void abortRequest() {
190                // Nothing to abort, since requests are immediate.
191            }
192
193            public ManagedClientConnection getConnection(
194                    long timeout, TimeUnit tunit) {
195                return SingleClientConnManager.this.getConnection(
196                        route, state);
197            }
198
199        };
200    }
201
202
203    /**
204     * Obtains a connection.
205     * This method does not block.
206     *
207     * @param route     where the connection should point to
208     *
209     * @return  a connection that can be used to communicate
210     *          along the given route
211     */
212    public ManagedClientConnection getConnection(HttpRoute route, Object state) {
213
214        if (route == null) {
215            throw new IllegalArgumentException("Route may not be null.");
216        }
217        assertStillUp();
218
219        if (log.isDebugEnabled()) {
220            log.debug("Get connection for route " + route);
221        }
222
223        if (managedConn != null)
224            revokeConnection();
225
226        // check re-usability of the connection
227        boolean recreate = false;
228        boolean shutdown = false;
229
230        // Kill the connection if it expired.
231        closeExpiredConnections();
232
233        if (uniquePoolEntry.connection.isOpen()) {
234            RouteTracker tracker = uniquePoolEntry.tracker;
235            shutdown = (tracker == null || // can happen if method is aborted
236                        !tracker.toRoute().equals(route));
237        } else {
238            // If the connection is not open, create a new PoolEntry,
239            // as the connection may have been marked not reusable,
240            // due to aborts -- and the PoolEntry should not be reused
241            // either.  There's no harm in recreating an entry if
242            // the connection is closed.
243            recreate = true;
244        }
245
246        if (shutdown) {
247            recreate = true;
248            try {
249                uniquePoolEntry.shutdown();
250            } catch (IOException iox) {
251                log.debug("Problem shutting down connection.", iox);
252            }
253        }
254
255        if (recreate)
256            uniquePoolEntry = new PoolEntry();
257
258        // BEGIN android-changed
259        // When using a recycled Socket, we need to re-tag it with any
260        // updated statistics options.
261        try {
262            final Socket socket = uniquePoolEntry.connection.getSocket();
263            if (socket != null) {
264                SocketTagger.get().tag(socket);
265            }
266        } catch (IOException iox) {
267            log.debug("Problem tagging socket.", iox);
268        }
269        // END android-changed
270
271        managedConn = new ConnAdapter(uniquePoolEntry, route);
272
273        return managedConn;
274    }
275
276
277    // non-javadoc, see interface ClientConnectionManager
278    public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
279        assertStillUp();
280
281        if (!(conn instanceof ConnAdapter)) {
282            throw new IllegalArgumentException
283                ("Connection class mismatch, " +
284                 "connection not obtained from this manager.");
285        }
286
287        if (log.isDebugEnabled()) {
288            log.debug("Releasing connection " + conn);
289        }
290
291        ConnAdapter sca = (ConnAdapter) conn;
292        if (sca.poolEntry == null)
293            return; // already released
294        ClientConnectionManager manager = sca.getManager();
295        if (manager != null && manager != this) {
296            throw new IllegalArgumentException
297                ("Connection not obtained from this manager.");
298        }
299
300        try {
301            // BEGIN android-changed
302            // When recycling a Socket, we un-tag it to avoid collecting
303            // statistics from future users.
304            final Socket socket = uniquePoolEntry.connection.getSocket();
305            if (socket != null) {
306                SocketTagger.get().untag(socket);
307            }
308            // END android-changed
309
310            // make sure that the response has been read completely
311            if (sca.isOpen() && (this.alwaysShutDown ||
312                                 !sca.isMarkedReusable())
313                ) {
314                if (log.isDebugEnabled()) {
315                    log.debug
316                        ("Released connection open but not reusable.");
317                }
318
319                // make sure this connection will not be re-used
320                // we might have gotten here because of a shutdown trigger
321                // shutdown of the adapter also clears the tracked route
322                sca.shutdown();
323            }
324        } catch (IOException iox) {
325            //@@@ log as warning? let pass?
326            if (log.isDebugEnabled())
327                log.debug("Exception shutting down released connection.",
328                          iox);
329        } finally {
330            sca.detach();
331            managedConn = null;
332            lastReleaseTime = System.currentTimeMillis();
333            if(validDuration > 0)
334                connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
335            else
336                connectionExpiresTime = Long.MAX_VALUE;
337        }
338    } // releaseConnection
339
340    public void closeExpiredConnections() {
341        if(System.currentTimeMillis() >= connectionExpiresTime) {
342            closeIdleConnections(0, TimeUnit.MILLISECONDS);
343        }
344    }
345
346
347    // non-javadoc, see interface ClientConnectionManager
348    public void closeIdleConnections(long idletime, TimeUnit tunit) {
349        assertStillUp();
350
351        // idletime can be 0 or negative, no problem there
352        if (tunit == null) {
353            throw new IllegalArgumentException("Time unit must not be null.");
354        }
355
356        if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
357            final long cutoff =
358                System.currentTimeMillis() - tunit.toMillis(idletime);
359            if (lastReleaseTime <= cutoff) {
360                try {
361                    uniquePoolEntry.close();
362                } catch (IOException iox) {
363                    // ignore
364                    log.debug("Problem closing idle connection.", iox);
365                }
366            }
367        }
368    }
369
370
371    // non-javadoc, see interface ClientConnectionManager
372    public void shutdown() {
373
374        this.isShutDown = true;
375
376        if (managedConn != null)
377            managedConn.detach();
378
379        try {
380            if (uniquePoolEntry != null) // and connection open?
381                uniquePoolEntry.shutdown();
382        } catch (IOException iox) {
383            // ignore
384            log.debug("Problem while shutting down manager.", iox);
385        } finally {
386            uniquePoolEntry = null;
387        }
388    }
389
390
391    /**
392     * Revokes the currently issued connection.
393     * The adapter gets disconnected, the connection will be shut down.
394     */
395    protected void revokeConnection() {
396        if (managedConn == null)
397            return;
398
399        log.warn(MISUSE_MESSAGE);
400
401        managedConn.detach();
402
403        try {
404            uniquePoolEntry.shutdown();
405        } catch (IOException iox) {
406            // ignore
407            log.debug("Problem while shutting down connection.", iox);
408        }
409    }
410
411
412    /**
413     * The pool entry for this connection manager.
414     */
415    protected class PoolEntry extends AbstractPoolEntry {
416
417        /**
418         * Creates a new pool entry.
419         *
420         */
421        protected PoolEntry() {
422            super(SingleClientConnManager.this.connOperator, null);
423        }
424
425        /**
426         * Closes the connection in this pool entry.
427         */
428        protected void close()
429            throws IOException {
430
431            shutdownEntry();
432            if (connection.isOpen())
433                connection.close();
434        }
435
436
437        /**
438         * Shuts down the connection in this pool entry.
439         */
440        protected void shutdown()
441            throws IOException {
442
443            shutdownEntry();
444            if (connection.isOpen())
445                connection.shutdown();
446        }
447
448    } // class PoolEntry
449
450
451
452    /**
453     * The connection adapter used by this manager.
454     */
455    protected class ConnAdapter extends AbstractPooledConnAdapter {
456
457        /**
458         * Creates a new connection adapter.
459         *
460         * @param entry   the pool entry for the connection being wrapped
461         * @param route   the planned route for this connection
462         */
463        protected ConnAdapter(PoolEntry entry, HttpRoute route) {
464            super(SingleClientConnManager.this, entry);
465            markReusable();
466            entry.route = route;
467        }
468
469    }
470
471
472} // class SingleClientConnManager
473