1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,423 @@ 1.4 +/* 1.5 + * ==================================================================== 1.6 + * Licensed to the Apache Software Foundation (ASF) under one 1.7 + * or more contributor license agreements. See the NOTICE file 1.8 + * distributed with this work for additional information 1.9 + * regarding copyright ownership. The ASF licenses this file 1.10 + * to you under the Apache License, Version 2.0 (the 1.11 + * "License"); you may not use this file except in compliance 1.12 + * with the License. You may obtain a copy of the License at 1.13 + * 1.14 + * http://www.apache.org/licenses/LICENSE-2.0 1.15 + * 1.16 + * Unless required by applicable law or agreed to in writing, 1.17 + * software distributed under the License is distributed on an 1.18 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1.19 + * KIND, either express or implied. See the License for the 1.20 + * specific language governing permissions and limitations 1.21 + * under the License. 1.22 + * ==================================================================== 1.23 + * 1.24 + * This software consists of voluntary contributions made by many 1.25 + * individuals on behalf of the Apache Software Foundation. For more 1.26 + * information on the Apache Software Foundation, please see 1.27 + * <http://www.apache.org/>. 1.28 + * 1.29 + */ 1.30 + 1.31 +package ch.boye.httpclientandroidlib.impl.conn; 1.32 + 1.33 +import java.io.IOException; 1.34 +import java.util.concurrent.TimeUnit; 1.35 + 1.36 +import ch.boye.httpclientandroidlib.annotation.GuardedBy; 1.37 +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; 1.38 + 1.39 +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; 1.40 +/* LogFactory removed by HttpClient for Android script. */ 1.41 +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; 1.42 +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; 1.43 +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; 1.44 +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; 1.45 +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; 1.46 +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; 1.47 +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; 1.48 +import ch.boye.httpclientandroidlib.params.HttpParams; 1.49 + 1.50 +/** 1.51 + * A connection manager for a single connection. This connection manager 1.52 + * maintains only one active connection at a time. Even though this class 1.53 + * is thread-safe it ought to be used by one execution thread only. 1.54 + * <p> 1.55 + * SingleClientConnManager will make an effort to reuse the connection 1.56 + * for subsequent requests with the same {@link HttpRoute route}. 1.57 + * It will, however, close the existing connection and open it 1.58 + * for the given route, if the route of the persistent connection does 1.59 + * not match that of the connection request. If the connection has been 1.60 + * already been allocated {@link IllegalStateException} is thrown. 1.61 + * 1.62 + * @since 4.0 1.63 + */ 1.64 +@ThreadSafe 1.65 +public class SingleClientConnManager implements ClientConnectionManager { 1.66 + 1.67 + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); 1.68 + 1.69 + /** The message to be logged on multiple allocation. */ 1.70 + public final static String MISUSE_MESSAGE = 1.71 + "Invalid use of SingleClientConnManager: connection still allocated.\n" + 1.72 + "Make sure to release the connection before allocating another one."; 1.73 + 1.74 + /** The schemes supported by this connection manager. */ 1.75 + protected final SchemeRegistry schemeRegistry; 1.76 + 1.77 + /** The operator for opening and updating connections. */ 1.78 + protected final ClientConnectionOperator connOperator; 1.79 + 1.80 + /** Whether the connection should be shut down on release. */ 1.81 + protected final boolean alwaysShutDown; 1.82 + 1.83 + /** The one and only entry in this pool. */ 1.84 + @GuardedBy("this") 1.85 + protected PoolEntry uniquePoolEntry; 1.86 + 1.87 + /** The currently issued managed connection, if any. */ 1.88 + @GuardedBy("this") 1.89 + protected ConnAdapter managedConn; 1.90 + 1.91 + /** The time of the last connection release, or -1. */ 1.92 + @GuardedBy("this") 1.93 + protected long lastReleaseTime; 1.94 + 1.95 + /** The time the last released connection expires and shouldn't be reused. */ 1.96 + @GuardedBy("this") 1.97 + protected long connectionExpiresTime; 1.98 + 1.99 + /** Indicates whether this connection manager is shut down. */ 1.100 + protected volatile boolean isShutDown; 1.101 + 1.102 + /** 1.103 + * Creates a new simple connection manager. 1.104 + * 1.105 + * @param params the parameters for this manager 1.106 + * @param schreg the scheme registry 1.107 + * 1.108 + * @deprecated use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)} 1.109 + */ 1.110 + @Deprecated 1.111 + public SingleClientConnManager(HttpParams params, 1.112 + SchemeRegistry schreg) { 1.113 + this(schreg); 1.114 + } 1.115 + /** 1.116 + * Creates a new simple connection manager. 1.117 + * 1.118 + * @param schreg the scheme registry 1.119 + */ 1.120 + public SingleClientConnManager(final SchemeRegistry schreg) { 1.121 + if (schreg == null) { 1.122 + throw new IllegalArgumentException 1.123 + ("Scheme registry must not be null."); 1.124 + } 1.125 + this.schemeRegistry = schreg; 1.126 + this.connOperator = createConnectionOperator(schreg); 1.127 + this.uniquePoolEntry = new PoolEntry(); 1.128 + this.managedConn = null; 1.129 + this.lastReleaseTime = -1L; 1.130 + this.alwaysShutDown = false; //@@@ from params? as argument? 1.131 + this.isShutDown = false; 1.132 + } 1.133 + 1.134 + /** 1.135 + * @since 4.1 1.136 + */ 1.137 + public SingleClientConnManager() { 1.138 + this(SchemeRegistryFactory.createDefault()); 1.139 + } 1.140 + 1.141 + @Override 1.142 + protected void finalize() throws Throwable { 1.143 + try { 1.144 + shutdown(); 1.145 + } finally { // Make sure we call overridden method even if shutdown barfs 1.146 + super.finalize(); 1.147 + } 1.148 + } 1.149 + 1.150 + public SchemeRegistry getSchemeRegistry() { 1.151 + return this.schemeRegistry; 1.152 + } 1.153 + 1.154 + /** 1.155 + * Hook for creating the connection operator. 1.156 + * It is called by the constructor. 1.157 + * Derived classes can override this method to change the 1.158 + * instantiation of the operator. 1.159 + * The default implementation here instantiates 1.160 + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. 1.161 + * 1.162 + * @param schreg the scheme registry to use, or <code>null</code> 1.163 + * 1.164 + * @return the connection operator to use 1.165 + */ 1.166 + protected ClientConnectionOperator 1.167 + createConnectionOperator(SchemeRegistry schreg) { 1.168 + return new DefaultClientConnectionOperator(schreg); 1.169 + } 1.170 + 1.171 + /** 1.172 + * Asserts that this manager is not shut down. 1.173 + * 1.174 + * @throws IllegalStateException if this manager is shut down 1.175 + */ 1.176 + protected final void assertStillUp() throws IllegalStateException { 1.177 + if (this.isShutDown) 1.178 + throw new IllegalStateException("Manager is shut down."); 1.179 + } 1.180 + 1.181 + public final ClientConnectionRequest requestConnection( 1.182 + final HttpRoute route, 1.183 + final Object state) { 1.184 + 1.185 + return new ClientConnectionRequest() { 1.186 + 1.187 + public void abortRequest() { 1.188 + // Nothing to abort, since requests are immediate. 1.189 + } 1.190 + 1.191 + public ManagedClientConnection getConnection( 1.192 + long timeout, TimeUnit tunit) { 1.193 + return SingleClientConnManager.this.getConnection( 1.194 + route, state); 1.195 + } 1.196 + 1.197 + }; 1.198 + } 1.199 + 1.200 + /** 1.201 + * Obtains a connection. 1.202 + * 1.203 + * @param route where the connection should point to 1.204 + * 1.205 + * @return a connection that can be used to communicate 1.206 + * along the given route 1.207 + */ 1.208 + public synchronized ManagedClientConnection getConnection(HttpRoute route, Object state) { 1.209 + if (route == null) { 1.210 + throw new IllegalArgumentException("Route may not be null."); 1.211 + } 1.212 + assertStillUp(); 1.213 + 1.214 + if (log.isDebugEnabled()) { 1.215 + log.debug("Get connection for route " + route); 1.216 + } 1.217 + 1.218 + if (managedConn != null) 1.219 + throw new IllegalStateException(MISUSE_MESSAGE); 1.220 + 1.221 + // check re-usability of the connection 1.222 + boolean recreate = false; 1.223 + boolean shutdown = false; 1.224 + 1.225 + // Kill the connection if it expired. 1.226 + closeExpiredConnections(); 1.227 + 1.228 + if (uniquePoolEntry.connection.isOpen()) { 1.229 + RouteTracker tracker = uniquePoolEntry.tracker; 1.230 + shutdown = (tracker == null || // can happen if method is aborted 1.231 + !tracker.toRoute().equals(route)); 1.232 + } else { 1.233 + // If the connection is not open, create a new PoolEntry, 1.234 + // as the connection may have been marked not reusable, 1.235 + // due to aborts -- and the PoolEntry should not be reused 1.236 + // either. There's no harm in recreating an entry if 1.237 + // the connection is closed. 1.238 + recreate = true; 1.239 + } 1.240 + 1.241 + if (shutdown) { 1.242 + recreate = true; 1.243 + try { 1.244 + uniquePoolEntry.shutdown(); 1.245 + } catch (IOException iox) { 1.246 + log.debug("Problem shutting down connection.", iox); 1.247 + } 1.248 + } 1.249 + 1.250 + if (recreate) 1.251 + uniquePoolEntry = new PoolEntry(); 1.252 + 1.253 + managedConn = new ConnAdapter(uniquePoolEntry, route); 1.254 + 1.255 + return managedConn; 1.256 + } 1.257 + 1.258 + public synchronized void releaseConnection( 1.259 + ManagedClientConnection conn, 1.260 + long validDuration, TimeUnit timeUnit) { 1.261 + assertStillUp(); 1.262 + 1.263 + if (!(conn instanceof ConnAdapter)) { 1.264 + throw new IllegalArgumentException 1.265 + ("Connection class mismatch, " + 1.266 + "connection not obtained from this manager."); 1.267 + } 1.268 + 1.269 + if (log.isDebugEnabled()) { 1.270 + log.debug("Releasing connection " + conn); 1.271 + } 1.272 + 1.273 + ConnAdapter sca = (ConnAdapter) conn; 1.274 + if (sca.poolEntry == null) 1.275 + return; // already released 1.276 + ClientConnectionManager manager = sca.getManager(); 1.277 + if (manager != null && manager != this) { 1.278 + throw new IllegalArgumentException 1.279 + ("Connection not obtained from this manager."); 1.280 + } 1.281 + 1.282 + try { 1.283 + // make sure that the response has been read completely 1.284 + if (sca.isOpen() && (this.alwaysShutDown || 1.285 + !sca.isMarkedReusable()) 1.286 + ) { 1.287 + if (log.isDebugEnabled()) { 1.288 + log.debug 1.289 + ("Released connection open but not reusable."); 1.290 + } 1.291 + 1.292 + // make sure this connection will not be re-used 1.293 + // we might have gotten here because of a shutdown trigger 1.294 + // shutdown of the adapter also clears the tracked route 1.295 + sca.shutdown(); 1.296 + } 1.297 + } catch (IOException iox) { 1.298 + if (log.isDebugEnabled()) 1.299 + log.debug("Exception shutting down released connection.", 1.300 + iox); 1.301 + } finally { 1.302 + sca.detach(); 1.303 + managedConn = null; 1.304 + lastReleaseTime = System.currentTimeMillis(); 1.305 + if(validDuration > 0) 1.306 + connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime; 1.307 + else 1.308 + connectionExpiresTime = Long.MAX_VALUE; 1.309 + } 1.310 + } 1.311 + 1.312 + public synchronized void closeExpiredConnections() { 1.313 + if(System.currentTimeMillis() >= connectionExpiresTime) { 1.314 + closeIdleConnections(0, TimeUnit.MILLISECONDS); 1.315 + } 1.316 + } 1.317 + 1.318 + public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) { 1.319 + assertStillUp(); 1.320 + 1.321 + // idletime can be 0 or negative, no problem there 1.322 + if (tunit == null) { 1.323 + throw new IllegalArgumentException("Time unit must not be null."); 1.324 + } 1.325 + 1.326 + if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) { 1.327 + final long cutoff = 1.328 + System.currentTimeMillis() - tunit.toMillis(idletime); 1.329 + if (lastReleaseTime <= cutoff) { 1.330 + try { 1.331 + uniquePoolEntry.close(); 1.332 + } catch (IOException iox) { 1.333 + // ignore 1.334 + log.debug("Problem closing idle connection.", iox); 1.335 + } 1.336 + } 1.337 + } 1.338 + } 1.339 + 1.340 + public synchronized void shutdown() { 1.341 + 1.342 + this.isShutDown = true; 1.343 + 1.344 + if (managedConn != null) 1.345 + managedConn.detach(); 1.346 + 1.347 + try { 1.348 + if (uniquePoolEntry != null) // and connection open? 1.349 + uniquePoolEntry.shutdown(); 1.350 + } catch (IOException iox) { 1.351 + // ignore 1.352 + log.debug("Problem while shutting down manager.", iox); 1.353 + } finally { 1.354 + uniquePoolEntry = null; 1.355 + } 1.356 + } 1.357 + 1.358 + /** 1.359 + * @deprecated no longer used 1.360 + */ 1.361 + @Deprecated 1.362 + protected synchronized void revokeConnection() { 1.363 + if (managedConn == null) 1.364 + return; 1.365 + managedConn.detach(); 1.366 + try { 1.367 + uniquePoolEntry.shutdown(); 1.368 + } catch (IOException iox) { 1.369 + // ignore 1.370 + log.debug("Problem while shutting down connection.", iox); 1.371 + } 1.372 + } 1.373 + 1.374 + /** 1.375 + * The pool entry for this connection manager. 1.376 + */ 1.377 + protected class PoolEntry extends AbstractPoolEntry { 1.378 + 1.379 + /** 1.380 + * Creates a new pool entry. 1.381 + * 1.382 + */ 1.383 + protected PoolEntry() { 1.384 + super(SingleClientConnManager.this.connOperator, null); 1.385 + } 1.386 + 1.387 + /** 1.388 + * Closes the connection in this pool entry. 1.389 + */ 1.390 + protected void close() throws IOException { 1.391 + shutdownEntry(); 1.392 + if (connection.isOpen()) 1.393 + connection.close(); 1.394 + } 1.395 + 1.396 + /** 1.397 + * Shuts down the connection in this pool entry. 1.398 + */ 1.399 + protected void shutdown() throws IOException { 1.400 + shutdownEntry(); 1.401 + if (connection.isOpen()) 1.402 + connection.shutdown(); 1.403 + } 1.404 + 1.405 + } 1.406 + 1.407 + /** 1.408 + * The connection adapter used by this manager. 1.409 + */ 1.410 + protected class ConnAdapter extends AbstractPooledConnAdapter { 1.411 + 1.412 + /** 1.413 + * Creates a new connection adapter. 1.414 + * 1.415 + * @param entry the pool entry for the connection being wrapped 1.416 + * @param route the planned route for this connection 1.417 + */ 1.418 + protected ConnAdapter(PoolEntry entry, HttpRoute route) { 1.419 + super(SingleClientConnManager.this, entry); 1.420 + markReusable(); 1.421 + entry.route = route; 1.422 + } 1.423 + 1.424 + } 1.425 + 1.426 +}