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