mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * ====================================================================
michael@0 3 * Licensed to the Apache Software Foundation (ASF) under one
michael@0 4 * or more contributor license agreements. See the NOTICE file
michael@0 5 * distributed with this work for additional information
michael@0 6 * regarding copyright ownership. The ASF licenses this file
michael@0 7 * to you under the Apache License, Version 2.0 (the
michael@0 8 * "License"); you may not use this file except in compliance
michael@0 9 * with the License. You may obtain a copy of the License at
michael@0 10 *
michael@0 11 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 12 *
michael@0 13 * Unless required by applicable law or agreed to in writing,
michael@0 14 * software distributed under the License is distributed on an
michael@0 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
michael@0 16 * KIND, either express or implied. See the License for the
michael@0 17 * specific language governing permissions and limitations
michael@0 18 * under the License.
michael@0 19 * ====================================================================
michael@0 20 *
michael@0 21 * This software consists of voluntary contributions made by many
michael@0 22 * individuals on behalf of the Apache Software Foundation. For more
michael@0 23 * information on the Apache Software Foundation, please see
michael@0 24 * <http://www.apache.org/>.
michael@0 25 *
michael@0 26 */
michael@0 27
michael@0 28 package ch.boye.httpclientandroidlib.impl.conn;
michael@0 29
michael@0 30 import java.io.IOException;
michael@0 31 import java.util.concurrent.TimeUnit;
michael@0 32
michael@0 33 import ch.boye.httpclientandroidlib.annotation.GuardedBy;
michael@0 34 import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
michael@0 35
michael@0 36 import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
michael@0 37 /* LogFactory removed by HttpClient for Android script. */
michael@0 38 import ch.boye.httpclientandroidlib.conn.ClientConnectionManager;
michael@0 39 import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator;
michael@0 40 import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest;
michael@0 41 import ch.boye.httpclientandroidlib.conn.ManagedClientConnection;
michael@0 42 import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
michael@0 43 import ch.boye.httpclientandroidlib.conn.routing.RouteTracker;
michael@0 44 import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry;
michael@0 45 import ch.boye.httpclientandroidlib.params.HttpParams;
michael@0 46
michael@0 47 /**
michael@0 48 * A connection manager for a single connection. This connection manager
michael@0 49 * maintains only one active connection at a time. Even though this class
michael@0 50 * is thread-safe it ought to be used by one execution thread only.
michael@0 51 * <p>
michael@0 52 * SingleClientConnManager will make an effort to reuse the connection
michael@0 53 * for subsequent requests with the same {@link HttpRoute route}.
michael@0 54 * It will, however, close the existing connection and open it
michael@0 55 * for the given route, if the route of the persistent connection does
michael@0 56 * not match that of the connection request. If the connection has been
michael@0 57 * already been allocated {@link IllegalStateException} is thrown.
michael@0 58 *
michael@0 59 * @since 4.0
michael@0 60 */
michael@0 61 @ThreadSafe
michael@0 62 public class SingleClientConnManager implements ClientConnectionManager {
michael@0 63
michael@0 64 public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
michael@0 65
michael@0 66 /** The message to be logged on multiple allocation. */
michael@0 67 public final static String MISUSE_MESSAGE =
michael@0 68 "Invalid use of SingleClientConnManager: connection still allocated.\n" +
michael@0 69 "Make sure to release the connection before allocating another one.";
michael@0 70
michael@0 71 /** The schemes supported by this connection manager. */
michael@0 72 protected final SchemeRegistry schemeRegistry;
michael@0 73
michael@0 74 /** The operator for opening and updating connections. */
michael@0 75 protected final ClientConnectionOperator connOperator;
michael@0 76
michael@0 77 /** Whether the connection should be shut down on release. */
michael@0 78 protected final boolean alwaysShutDown;
michael@0 79
michael@0 80 /** The one and only entry in this pool. */
michael@0 81 @GuardedBy("this")
michael@0 82 protected PoolEntry uniquePoolEntry;
michael@0 83
michael@0 84 /** The currently issued managed connection, if any. */
michael@0 85 @GuardedBy("this")
michael@0 86 protected ConnAdapter managedConn;
michael@0 87
michael@0 88 /** The time of the last connection release, or -1. */
michael@0 89 @GuardedBy("this")
michael@0 90 protected long lastReleaseTime;
michael@0 91
michael@0 92 /** The time the last released connection expires and shouldn't be reused. */
michael@0 93 @GuardedBy("this")
michael@0 94 protected long connectionExpiresTime;
michael@0 95
michael@0 96 /** Indicates whether this connection manager is shut down. */
michael@0 97 protected volatile boolean isShutDown;
michael@0 98
michael@0 99 /**
michael@0 100 * Creates a new simple connection manager.
michael@0 101 *
michael@0 102 * @param params the parameters for this manager
michael@0 103 * @param schreg the scheme registry
michael@0 104 *
michael@0 105 * @deprecated use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)}
michael@0 106 */
michael@0 107 @Deprecated
michael@0 108 public SingleClientConnManager(HttpParams params,
michael@0 109 SchemeRegistry schreg) {
michael@0 110 this(schreg);
michael@0 111 }
michael@0 112 /**
michael@0 113 * Creates a new simple connection manager.
michael@0 114 *
michael@0 115 * @param schreg the scheme registry
michael@0 116 */
michael@0 117 public SingleClientConnManager(final SchemeRegistry schreg) {
michael@0 118 if (schreg == null) {
michael@0 119 throw new IllegalArgumentException
michael@0 120 ("Scheme registry must not be null.");
michael@0 121 }
michael@0 122 this.schemeRegistry = schreg;
michael@0 123 this.connOperator = createConnectionOperator(schreg);
michael@0 124 this.uniquePoolEntry = new PoolEntry();
michael@0 125 this.managedConn = null;
michael@0 126 this.lastReleaseTime = -1L;
michael@0 127 this.alwaysShutDown = false; //@@@ from params? as argument?
michael@0 128 this.isShutDown = false;
michael@0 129 }
michael@0 130
michael@0 131 /**
michael@0 132 * @since 4.1
michael@0 133 */
michael@0 134 public SingleClientConnManager() {
michael@0 135 this(SchemeRegistryFactory.createDefault());
michael@0 136 }
michael@0 137
michael@0 138 @Override
michael@0 139 protected void finalize() throws Throwable {
michael@0 140 try {
michael@0 141 shutdown();
michael@0 142 } finally { // Make sure we call overridden method even if shutdown barfs
michael@0 143 super.finalize();
michael@0 144 }
michael@0 145 }
michael@0 146
michael@0 147 public SchemeRegistry getSchemeRegistry() {
michael@0 148 return this.schemeRegistry;
michael@0 149 }
michael@0 150
michael@0 151 /**
michael@0 152 * Hook for creating the connection operator.
michael@0 153 * It is called by the constructor.
michael@0 154 * Derived classes can override this method to change the
michael@0 155 * instantiation of the operator.
michael@0 156 * The default implementation here instantiates
michael@0 157 * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
michael@0 158 *
michael@0 159 * @param schreg the scheme registry to use, or <code>null</code>
michael@0 160 *
michael@0 161 * @return the connection operator to use
michael@0 162 */
michael@0 163 protected ClientConnectionOperator
michael@0 164 createConnectionOperator(SchemeRegistry schreg) {
michael@0 165 return new DefaultClientConnectionOperator(schreg);
michael@0 166 }
michael@0 167
michael@0 168 /**
michael@0 169 * Asserts that this manager is not shut down.
michael@0 170 *
michael@0 171 * @throws IllegalStateException if this manager is shut down
michael@0 172 */
michael@0 173 protected final void assertStillUp() throws IllegalStateException {
michael@0 174 if (this.isShutDown)
michael@0 175 throw new IllegalStateException("Manager is shut down.");
michael@0 176 }
michael@0 177
michael@0 178 public final ClientConnectionRequest requestConnection(
michael@0 179 final HttpRoute route,
michael@0 180 final Object state) {
michael@0 181
michael@0 182 return new ClientConnectionRequest() {
michael@0 183
michael@0 184 public void abortRequest() {
michael@0 185 // Nothing to abort, since requests are immediate.
michael@0 186 }
michael@0 187
michael@0 188 public ManagedClientConnection getConnection(
michael@0 189 long timeout, TimeUnit tunit) {
michael@0 190 return SingleClientConnManager.this.getConnection(
michael@0 191 route, state);
michael@0 192 }
michael@0 193
michael@0 194 };
michael@0 195 }
michael@0 196
michael@0 197 /**
michael@0 198 * Obtains a connection.
michael@0 199 *
michael@0 200 * @param route where the connection should point to
michael@0 201 *
michael@0 202 * @return a connection that can be used to communicate
michael@0 203 * along the given route
michael@0 204 */
michael@0 205 public synchronized ManagedClientConnection getConnection(HttpRoute route, Object state) {
michael@0 206 if (route == null) {
michael@0 207 throw new IllegalArgumentException("Route may not be null.");
michael@0 208 }
michael@0 209 assertStillUp();
michael@0 210
michael@0 211 if (log.isDebugEnabled()) {
michael@0 212 log.debug("Get connection for route " + route);
michael@0 213 }
michael@0 214
michael@0 215 if (managedConn != null)
michael@0 216 throw new IllegalStateException(MISUSE_MESSAGE);
michael@0 217
michael@0 218 // check re-usability of the connection
michael@0 219 boolean recreate = false;
michael@0 220 boolean shutdown = false;
michael@0 221
michael@0 222 // Kill the connection if it expired.
michael@0 223 closeExpiredConnections();
michael@0 224
michael@0 225 if (uniquePoolEntry.connection.isOpen()) {
michael@0 226 RouteTracker tracker = uniquePoolEntry.tracker;
michael@0 227 shutdown = (tracker == null || // can happen if method is aborted
michael@0 228 !tracker.toRoute().equals(route));
michael@0 229 } else {
michael@0 230 // If the connection is not open, create a new PoolEntry,
michael@0 231 // as the connection may have been marked not reusable,
michael@0 232 // due to aborts -- and the PoolEntry should not be reused
michael@0 233 // either. There's no harm in recreating an entry if
michael@0 234 // the connection is closed.
michael@0 235 recreate = true;
michael@0 236 }
michael@0 237
michael@0 238 if (shutdown) {
michael@0 239 recreate = true;
michael@0 240 try {
michael@0 241 uniquePoolEntry.shutdown();
michael@0 242 } catch (IOException iox) {
michael@0 243 log.debug("Problem shutting down connection.", iox);
michael@0 244 }
michael@0 245 }
michael@0 246
michael@0 247 if (recreate)
michael@0 248 uniquePoolEntry = new PoolEntry();
michael@0 249
michael@0 250 managedConn = new ConnAdapter(uniquePoolEntry, route);
michael@0 251
michael@0 252 return managedConn;
michael@0 253 }
michael@0 254
michael@0 255 public synchronized void releaseConnection(
michael@0 256 ManagedClientConnection conn,
michael@0 257 long validDuration, TimeUnit timeUnit) {
michael@0 258 assertStillUp();
michael@0 259
michael@0 260 if (!(conn instanceof ConnAdapter)) {
michael@0 261 throw new IllegalArgumentException
michael@0 262 ("Connection class mismatch, " +
michael@0 263 "connection not obtained from this manager.");
michael@0 264 }
michael@0 265
michael@0 266 if (log.isDebugEnabled()) {
michael@0 267 log.debug("Releasing connection " + conn);
michael@0 268 }
michael@0 269
michael@0 270 ConnAdapter sca = (ConnAdapter) conn;
michael@0 271 if (sca.poolEntry == null)
michael@0 272 return; // already released
michael@0 273 ClientConnectionManager manager = sca.getManager();
michael@0 274 if (manager != null && manager != this) {
michael@0 275 throw new IllegalArgumentException
michael@0 276 ("Connection not obtained from this manager.");
michael@0 277 }
michael@0 278
michael@0 279 try {
michael@0 280 // make sure that the response has been read completely
michael@0 281 if (sca.isOpen() && (this.alwaysShutDown ||
michael@0 282 !sca.isMarkedReusable())
michael@0 283 ) {
michael@0 284 if (log.isDebugEnabled()) {
michael@0 285 log.debug
michael@0 286 ("Released connection open but not reusable.");
michael@0 287 }
michael@0 288
michael@0 289 // make sure this connection will not be re-used
michael@0 290 // we might have gotten here because of a shutdown trigger
michael@0 291 // shutdown of the adapter also clears the tracked route
michael@0 292 sca.shutdown();
michael@0 293 }
michael@0 294 } catch (IOException iox) {
michael@0 295 if (log.isDebugEnabled())
michael@0 296 log.debug("Exception shutting down released connection.",
michael@0 297 iox);
michael@0 298 } finally {
michael@0 299 sca.detach();
michael@0 300 managedConn = null;
michael@0 301 lastReleaseTime = System.currentTimeMillis();
michael@0 302 if(validDuration > 0)
michael@0 303 connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
michael@0 304 else
michael@0 305 connectionExpiresTime = Long.MAX_VALUE;
michael@0 306 }
michael@0 307 }
michael@0 308
michael@0 309 public synchronized void closeExpiredConnections() {
michael@0 310 if(System.currentTimeMillis() >= connectionExpiresTime) {
michael@0 311 closeIdleConnections(0, TimeUnit.MILLISECONDS);
michael@0 312 }
michael@0 313 }
michael@0 314
michael@0 315 public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) {
michael@0 316 assertStillUp();
michael@0 317
michael@0 318 // idletime can be 0 or negative, no problem there
michael@0 319 if (tunit == null) {
michael@0 320 throw new IllegalArgumentException("Time unit must not be null.");
michael@0 321 }
michael@0 322
michael@0 323 if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
michael@0 324 final long cutoff =
michael@0 325 System.currentTimeMillis() - tunit.toMillis(idletime);
michael@0 326 if (lastReleaseTime <= cutoff) {
michael@0 327 try {
michael@0 328 uniquePoolEntry.close();
michael@0 329 } catch (IOException iox) {
michael@0 330 // ignore
michael@0 331 log.debug("Problem closing idle connection.", iox);
michael@0 332 }
michael@0 333 }
michael@0 334 }
michael@0 335 }
michael@0 336
michael@0 337 public synchronized void shutdown() {
michael@0 338
michael@0 339 this.isShutDown = true;
michael@0 340
michael@0 341 if (managedConn != null)
michael@0 342 managedConn.detach();
michael@0 343
michael@0 344 try {
michael@0 345 if (uniquePoolEntry != null) // and connection open?
michael@0 346 uniquePoolEntry.shutdown();
michael@0 347 } catch (IOException iox) {
michael@0 348 // ignore
michael@0 349 log.debug("Problem while shutting down manager.", iox);
michael@0 350 } finally {
michael@0 351 uniquePoolEntry = null;
michael@0 352 }
michael@0 353 }
michael@0 354
michael@0 355 /**
michael@0 356 * @deprecated no longer used
michael@0 357 */
michael@0 358 @Deprecated
michael@0 359 protected synchronized void revokeConnection() {
michael@0 360 if (managedConn == null)
michael@0 361 return;
michael@0 362 managedConn.detach();
michael@0 363 try {
michael@0 364 uniquePoolEntry.shutdown();
michael@0 365 } catch (IOException iox) {
michael@0 366 // ignore
michael@0 367 log.debug("Problem while shutting down connection.", iox);
michael@0 368 }
michael@0 369 }
michael@0 370
michael@0 371 /**
michael@0 372 * The pool entry for this connection manager.
michael@0 373 */
michael@0 374 protected class PoolEntry extends AbstractPoolEntry {
michael@0 375
michael@0 376 /**
michael@0 377 * Creates a new pool entry.
michael@0 378 *
michael@0 379 */
michael@0 380 protected PoolEntry() {
michael@0 381 super(SingleClientConnManager.this.connOperator, null);
michael@0 382 }
michael@0 383
michael@0 384 /**
michael@0 385 * Closes the connection in this pool entry.
michael@0 386 */
michael@0 387 protected void close() throws IOException {
michael@0 388 shutdownEntry();
michael@0 389 if (connection.isOpen())
michael@0 390 connection.close();
michael@0 391 }
michael@0 392
michael@0 393 /**
michael@0 394 * Shuts down the connection in this pool entry.
michael@0 395 */
michael@0 396 protected void shutdown() throws IOException {
michael@0 397 shutdownEntry();
michael@0 398 if (connection.isOpen())
michael@0 399 connection.shutdown();
michael@0 400 }
michael@0 401
michael@0 402 }
michael@0 403
michael@0 404 /**
michael@0 405 * The connection adapter used by this manager.
michael@0 406 */
michael@0 407 protected class ConnAdapter extends AbstractPooledConnAdapter {
michael@0 408
michael@0 409 /**
michael@0 410 * Creates a new connection adapter.
michael@0 411 *
michael@0 412 * @param entry the pool entry for the connection being wrapped
michael@0 413 * @param route the planned route for this connection
michael@0 414 */
michael@0 415 protected ConnAdapter(PoolEntry entry, HttpRoute route) {
michael@0 416 super(SingleClientConnManager.this, entry);
michael@0 417 markReusable();
michael@0 418 entry.route = route;
michael@0 419 }
michael@0 420
michael@0 421 }
michael@0 422
michael@0 423 }

mercurial