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 32 package org.apache.http.impl.conn; 33 34 import java.io.IOException; 35 import java.net.Socket; 36 import java.util.concurrent.TimeUnit; 37 38 import android.net.TrafficStats; 39 import org.apache.commons.logging.Log; 40 import org.apache.commons.logging.LogFactory; 41 import org.apache.http.conn.ClientConnectionManager; 42 import org.apache.http.conn.ClientConnectionOperator; 43 import org.apache.http.conn.ClientConnectionRequest; 44 import org.apache.http.conn.ManagedClientConnection; 45 import org.apache.http.conn.routing.HttpRoute; 46 import org.apache.http.conn.routing.RouteTracker; 47 import org.apache.http.conn.scheme.SchemeRegistry; 48 import 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 77 public 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 */ SingleClientConnManager(HttpParams params, SchemeRegistry schreg)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 finalize()139 protected void finalize() throws Throwable { 140 shutdown(); 141 super.finalize(); 142 } 143 144 145 // non-javadoc, see interface ClientConnectionManager getSchemeRegistry()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 createConnectionOperator(SchemeRegistry schreg)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 */ assertStillUp()175 protected final void assertStillUp() 176 throws IllegalStateException { 177 178 if (this.isShutDown) 179 throw new IllegalStateException("Manager is shut down."); 180 } 181 182 requestConnection( final HttpRoute route, final Object state)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 TrafficStats.tagSocket(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 TrafficStats.untagSocket(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