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/RouteSpecificPool.java $
      3  * $Revision: 677240 $
      4  * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 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.util.ListIterator;
     35 import java.util.Queue;
     36 import java.util.LinkedList;
     37 
     38 import org.apache.commons.logging.Log;
     39 import org.apache.commons.logging.LogFactory;
     40 import org.apache.http.conn.OperatedClientConnection;
     41 import org.apache.http.conn.routing.HttpRoute;
     42 import org.apache.http.util.LangUtils;
     43 
     44 
     45 /**
     46  * A connection sub-pool for a specific route, used by {@link ConnPoolByRoute}.
     47  * The methods in this class are unsynchronized. It is expected that the
     48  * containing pool takes care of synchronization.
     49  */
     50 public class RouteSpecificPool {
     51 
     52     private final Log log = LogFactory.getLog(getClass());
     53 
     54     /** The route this pool is for. */
     55     protected final HttpRoute route;
     56 
     57     /** the maximum number of entries allowed for this pool */
     58     protected final int maxEntries;
     59 
     60     /**
     61      * The list of free entries.
     62      * This list is managed LIFO, to increase idle times and
     63      * allow for closing connections that are not really needed.
     64      */
     65     protected final LinkedList<BasicPoolEntry> freeEntries;
     66 
     67     /** The list of threads waiting for this pool. */
     68     protected final Queue<WaitingThread> waitingThreads;
     69 
     70     /** The number of created entries. */
     71     protected int numEntries;
     72 
     73 
     74     /**
     75      * Creates a new route-specific pool.
     76      *
     77      * @param route the route for which to pool
     78      * @param maxEntries the maximum number of entries allowed for this pool
     79      */
     80     public RouteSpecificPool(HttpRoute route, int maxEntries) {
     81         this.route = route;
     82         this.maxEntries = maxEntries;
     83         this.freeEntries = new LinkedList<BasicPoolEntry>();
     84         this.waitingThreads = new LinkedList<WaitingThread>();
     85         this.numEntries = 0;
     86     }
     87 
     88 
     89     /**
     90      * Obtains the route for which this pool is specific.
     91      *
     92      * @return  the route
     93      */
     94     public final HttpRoute getRoute() {
     95         return route;
     96     }
     97 
     98 
     99     /**
    100      * Obtains the maximum number of entries allowed for this pool.
    101      *
    102      * @return  the max entry number
    103      */
    104     public final int getMaxEntries() {
    105         return maxEntries;
    106     }
    107 
    108 
    109     /**
    110      * Indicates whether this pool is unused.
    111      * A pool is unused if there is neither an entry nor a waiting thread.
    112      * All entries count, not only the free but also the allocated ones.
    113      *
    114      * @return  <code>true</code> if this pool is unused,
    115      *          <code>false</code> otherwise
    116      */
    117     public boolean isUnused() {
    118         return (numEntries < 1) && waitingThreads.isEmpty();
    119     }
    120 
    121 
    122     /**
    123      * Return remaining capacity of this pool
    124      *
    125      * @return capacity
    126      */
    127     public int getCapacity() {
    128         return maxEntries - numEntries;
    129     }
    130 
    131 
    132     /**
    133      * Obtains the number of entries.
    134      * This includes not only the free entries, but also those that
    135      * have been created and are currently issued to an application.
    136      *
    137      * @return  the number of entries for the route of this pool
    138      */
    139     public final int getEntryCount() {
    140         return numEntries;
    141     }
    142 
    143 
    144     /**
    145      * Obtains a free entry from this pool, if one is available.
    146      *
    147      * @return an available pool entry, or <code>null</code> if there is none
    148      */
    149     public BasicPoolEntry allocEntry(final Object state) {
    150         if (!freeEntries.isEmpty()) {
    151             ListIterator<BasicPoolEntry> it = freeEntries.listIterator(freeEntries.size());
    152             while (it.hasPrevious()) {
    153                 BasicPoolEntry entry = it.previous();
    154                 if (LangUtils.equals(state, entry.getState())) {
    155                     it.remove();
    156                     return entry;
    157                 }
    158             }
    159         }
    160         if (!freeEntries.isEmpty()) {
    161             BasicPoolEntry entry = freeEntries.remove();
    162             entry.setState(null);
    163             OperatedClientConnection conn = entry.getConnection();
    164             try {
    165                 conn.close();
    166             } catch (IOException ex) {
    167                 log.debug("I/O error closing connection", ex);
    168             }
    169             return entry;
    170         }
    171         return null;
    172     }
    173 
    174 
    175     /**
    176      * Returns an allocated entry to this pool.
    177      *
    178      * @param entry     the entry obtained from {@link #allocEntry allocEntry}
    179      *                  or presented to {@link #createdEntry createdEntry}
    180      */
    181     public void freeEntry(BasicPoolEntry entry) {
    182 
    183         if (numEntries < 1) {
    184             throw new IllegalStateException
    185                 ("No entry created for this pool. " + route);
    186         }
    187         if (numEntries <= freeEntries.size()) {
    188             throw new IllegalStateException
    189                 ("No entry allocated from this pool. " + route);
    190         }
    191         freeEntries.add(entry);
    192     }
    193 
    194 
    195     /**
    196      * Indicates creation of an entry for this pool.
    197      * The entry will <i>not</i> be added to the list of free entries,
    198      * it is only recognized as belonging to this pool now. It can then
    199      * be passed to {@link #freeEntry freeEntry}.
    200      *
    201      * @param entry     the entry that was created for this pool
    202      */
    203     public void createdEntry(BasicPoolEntry entry) {
    204 
    205         if (!route.equals(entry.getPlannedRoute())) {
    206             throw new IllegalArgumentException
    207                 ("Entry not planned for this pool." +
    208                  "\npool: " + route +
    209                  "\nplan: " + entry.getPlannedRoute());
    210         }
    211 
    212         numEntries++;
    213     }
    214 
    215 
    216     /**
    217      * Deletes an entry from this pool.
    218      * Only entries that are currently free in this pool can be deleted.
    219      * Allocated entries can not be deleted.
    220      *
    221      * @param entry     the entry to delete from this pool
    222      *
    223      * @return  <code>true</code> if the entry was found and deleted, or
    224      *          <code>false</code> if the entry was not found
    225      */
    226     public boolean deleteEntry(BasicPoolEntry entry) {
    227 
    228         final boolean found = freeEntries.remove(entry);
    229         if (found)
    230             numEntries--;
    231         return found;
    232     }
    233 
    234 
    235     /**
    236      * Forgets about an entry from this pool.
    237      * This method is used to indicate that an entry
    238      * {@link #allocEntry allocated}
    239      * from this pool has been lost and will not be returned.
    240      */
    241     public void dropEntry() {
    242         if (numEntries < 1) {
    243             throw new IllegalStateException
    244                 ("There is no entry that could be dropped.");
    245         }
    246         numEntries--;
    247     }
    248 
    249 
    250     /**
    251      * Adds a waiting thread.
    252      * This pool makes no attempt to match waiting threads with pool entries.
    253      * It is the caller's responsibility to check that there is no entry
    254      * before adding a waiting thread.
    255      *
    256      * @param wt        the waiting thread
    257      */
    258     public void queueThread(WaitingThread wt) {
    259         if (wt == null) {
    260             throw new IllegalArgumentException
    261                 ("Waiting thread must not be null.");
    262         }
    263         this.waitingThreads.add(wt);
    264     }
    265 
    266 
    267     /**
    268      * Checks whether there is a waiting thread in this pool.
    269      *
    270      * @return  <code>true</code> if there is a waiting thread,
    271      *          <code>false</code> otherwise
    272      */
    273     public boolean hasThread() {
    274         return !this.waitingThreads.isEmpty();
    275     }
    276 
    277 
    278     /**
    279      * Returns the next thread in the queue.
    280      *
    281      * @return  a waiting thread, or <code>null</code> if there is none
    282      */
    283     public WaitingThread nextThread() {
    284         return this.waitingThreads.peek();
    285     }
    286 
    287 
    288     /**
    289      * Removes a waiting thread, if it is queued.
    290      *
    291      * @param wt        the waiting thread
    292      */
    293     public void removeThread(WaitingThread wt) {
    294         if (wt == null)
    295             return;
    296 
    297         this.waitingThreads.remove(wt);
    298     }
    299 
    300 
    301 } // class RouteSpecificPool
    302