Home | History | Annotate | Download | only in tsccm
      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      */
    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      */
    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
    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      */
    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      */
    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
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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