1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java $ 3 * $Revision: 673450 $ 4 * $Date: 2008-07-02 10:35:05 -0700 (Wed, 02 Jul 2008) $ 5 * 6 * ==================================================================== 7 * 8 * Licensed to the Apache Software Foundation (ASF) under one or more 9 * contributor license agreements. See the NOTICE file distributed with 10 * this work for additional information regarding copyright ownership. 11 * The ASF licenses this file to You under the Apache License, Version 2.0 12 * (the "License"); you may not use this file except in compliance with 13 * 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, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 * See the License for the specific language governing permissions and 21 * limitations under the License. 22 * ==================================================================== 23 * 24 * This software consists of voluntary contributions made by many 25 * individuals on behalf of the Apache Software Foundation. For more 26 * information on the Apache Software Foundation, please see 27 * <http://www.apache.org/>. 28 * 29 */ 30 31 package org.apache.http.impl.conn.tsccm; 32 33 import java.io.IOException; 34 import java.lang.ref.Reference; 35 import java.lang.ref.ReferenceQueue; 36 import java.util.Set; 37 import java.util.HashSet; 38 import java.util.Iterator; 39 import java.util.concurrent.TimeUnit; 40 import java.util.concurrent.locks.Lock; 41 import java.util.concurrent.locks.ReentrantLock; 42 43 import org.apache.commons.logging.Log; 44 import org.apache.commons.logging.LogFactory; 45 import org.apache.http.conn.ConnectionPoolTimeoutException; 46 import org.apache.http.conn.OperatedClientConnection; 47 import org.apache.http.conn.routing.HttpRoute; 48 import org.apache.http.impl.conn.IdleConnectionHandler; 49 50 51 /** 52 * An abstract connection pool. 53 * It is used by the {@link ThreadSafeClientConnManager}. 54 * The abstract pool includes a {@link #poolLock}, which is used to 55 * synchronize access to the internal pool datastructures. 56 * Don't use <code>synchronized</code> for that purpose! 57 */ 58 public abstract class AbstractConnPool implements RefQueueHandler { 59 60 private final Log log = LogFactory.getLog(getClass()); 61 62 /** 63 * The global lock for this pool. 64 */ 65 protected final Lock poolLock; 66 67 68 /** 69 * References to issued connections. 70 * Objects in this set are of class 71 * {@link BasicPoolEntryRef BasicPoolEntryRef}, 72 * and point to the pool entry for the issued connection. 73 * GCed connections are detected by the missing pool entries. 74 */ 75 protected Set<BasicPoolEntryRef> issuedConnections; 76 77 /** The handler for idle connections. */ 78 protected IdleConnectionHandler idleConnHandler; 79 80 /** The current total number of connections. */ 81 protected int numConnections; 82 83 /** 84 * A reference queue to track loss of pool entries to GC. 85 * The same queue is used to track loss of the connection manager, 86 * so we cannot specialize the type. 87 */ 88 protected ReferenceQueue<Object> refQueue; 89 90 /** A worker (thread) to track loss of pool entries to GC. */ 91 private RefQueueWorker refWorker; 92 93 94 /** Indicates whether this pool is shut down. */ 95 protected volatile boolean isShutDown; 96 97 /** 98 * Creates a new connection pool. 99 */ AbstractConnPool()100 protected AbstractConnPool() { 101 issuedConnections = new HashSet<BasicPoolEntryRef>(); 102 idleConnHandler = new IdleConnectionHandler(); 103 104 boolean fair = false; //@@@ check parameters to decide 105 poolLock = new ReentrantLock(fair); 106 } 107 108 109 /** 110 * Enables connection garbage collection (GC). 111 * This method must be called immediately after creating the 112 * connection pool. It is not possible to enable connection GC 113 * after pool entries have been created. Neither is it possible 114 * to disable connection GC. 115 * 116 * @throws IllegalStateException 117 * if connection GC is already enabled, or if it cannot be 118 * enabled because there already are pool entries 119 */ enableConnectionGC()120 public void enableConnectionGC() 121 throws IllegalStateException { 122 123 if (refQueue != null) { 124 throw new IllegalStateException("Connection GC already enabled."); 125 } 126 poolLock.lock(); 127 try { 128 if (numConnections > 0) { //@@@ is this check sufficient? 129 throw new IllegalStateException("Pool already in use."); 130 } 131 } finally { 132 poolLock.unlock(); 133 } 134 135 refQueue = new ReferenceQueue<Object>(); 136 refWorker = new RefQueueWorker(refQueue, this); 137 Thread t = new Thread(refWorker); //@@@ use a thread factory 138 t.setDaemon(true); 139 t.setName("RefQueueWorker@" + this); 140 t.start(); 141 } 142 143 144 /** 145 * Obtains a pool entry with a connection within the given timeout. 146 * 147 * @param route the route for which to get the connection 148 * @param timeout the timeout, 0 or negative for no timeout 149 * @param tunit the unit for the <code>timeout</code>, 150 * may be <code>null</code> only if there is no timeout 151 * 152 * @return pool entry holding a connection for the route 153 * 154 * @throws ConnectionPoolTimeoutException 155 * if the timeout expired 156 * @throws InterruptedException 157 * if the calling thread was interrupted 158 */ 159 public final getEntry( HttpRoute route, Object state, long timeout, TimeUnit tunit)160 BasicPoolEntry getEntry( 161 HttpRoute route, 162 Object state, 163 long timeout, 164 TimeUnit tunit) 165 throws ConnectionPoolTimeoutException, InterruptedException { 166 return requestPoolEntry(route, state).getPoolEntry(timeout, tunit); 167 } 168 169 /** 170 * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry} 171 * can be obtained, or the request can be aborted. 172 */ requestPoolEntry(HttpRoute route, Object state)173 public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state); 174 175 176 /** 177 * Returns an entry into the pool. 178 * The connection of the entry is expected to be in a suitable state, 179 * either open and re-usable, or closed. The pool will not make any 180 * attempt to determine whether it can be re-used or not. 181 * 182 * @param entry the entry for the connection to release 183 * @param reusable <code>true</code> if the entry is deemed 184 * reusable, <code>false</code> otherwise. 185 * @param validDuration The duration that the entry should remain free and reusable. 186 * @param timeUnit The unit of time the duration is measured in. 187 */ freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit)188 public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit) 189 ; 190 191 192 193 // non-javadoc, see interface RefQueueHandler 194 // BEGIN android-changed handleReference(Reference ref)195 public void handleReference(Reference ref) { 196 // END android-changed 197 poolLock.lock(); 198 try { 199 200 if (ref instanceof BasicPoolEntryRef) { 201 // check if the GCed pool entry was still in use 202 //@@@ find a way to detect this without lookup 203 //@@@ flag in the BasicPoolEntryRef, to be reset when freed? 204 final boolean lost = issuedConnections.remove(ref); 205 if (lost) { 206 final HttpRoute route = 207 ((BasicPoolEntryRef)ref).getRoute(); 208 if (log.isDebugEnabled()) { 209 log.debug("Connection garbage collected. " + route); 210 } 211 handleLostEntry(route); 212 } 213 } 214 215 } finally { 216 poolLock.unlock(); 217 } 218 } 219 220 221 /** 222 * Handles cleaning up for a lost pool entry with the given route. 223 * A lost pool entry corresponds to a connection that was 224 * garbage collected instead of being properly released. 225 * 226 * @param route the route of the pool entry that was lost 227 */ handleLostEntry(HttpRoute route)228 protected abstract void handleLostEntry(HttpRoute route) 229 ; 230 231 232 /** 233 * Closes idle connections. 234 * 235 * @param idletime the time the connections should have been idle 236 * in order to be closed now 237 * @param tunit the unit for the <code>idletime</code> 238 */ closeIdleConnections(long idletime, TimeUnit tunit)239 public void closeIdleConnections(long idletime, TimeUnit tunit) { 240 241 // idletime can be 0 or negative, no problem there 242 if (tunit == null) { 243 throw new IllegalArgumentException("Time unit must not be null."); 244 } 245 246 poolLock.lock(); 247 try { 248 idleConnHandler.closeIdleConnections(tunit.toMillis(idletime)); 249 } finally { 250 poolLock.unlock(); 251 } 252 } 253 closeExpiredConnections()254 public void closeExpiredConnections() { 255 poolLock.lock(); 256 try { 257 idleConnHandler.closeExpiredConnections(); 258 } finally { 259 poolLock.unlock(); 260 } 261 } 262 263 264 //@@@ revise this cleanup stuff (closeIdle+deleteClosed), it's not good 265 266 /** 267 * Deletes all entries for closed connections. 268 */ deleteClosedConnections()269 public abstract void deleteClosedConnections() 270 ; 271 272 273 /** 274 * Shuts down this pool and all associated resources. 275 * Overriding methods MUST call the implementation here! 276 */ shutdown()277 public void shutdown() { 278 279 poolLock.lock(); 280 try { 281 282 if (isShutDown) 283 return; 284 285 // no point in monitoring GC anymore 286 if (refWorker != null) 287 refWorker.shutdown(); 288 289 // close all connections that are issued to an application 290 Iterator<BasicPoolEntryRef> iter = issuedConnections.iterator(); 291 while (iter.hasNext()) { 292 BasicPoolEntryRef per = iter.next(); 293 iter.remove(); 294 BasicPoolEntry entry = per.get(); 295 if (entry != null) { 296 closeConnection(entry.getConnection()); 297 } 298 } 299 300 // remove all references to connections 301 //@@@ use this for shutting them down instead? 302 idleConnHandler.removeAll(); 303 304 isShutDown = true; 305 306 } finally { 307 poolLock.unlock(); 308 } 309 } 310 311 312 /** 313 * Closes a connection from this pool. 314 * 315 * @param conn the connection to close, or <code>null</code> 316 */ closeConnection(final OperatedClientConnection conn)317 protected void closeConnection(final OperatedClientConnection conn) { 318 if (conn != null) { 319 try { 320 conn.close(); 321 } catch (IOException ex) { 322 log.debug("I/O error closing connection", ex); 323 } 324 } 325 } 326 327 328 329 330 331 } // class AbstractConnPool 332 333