• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
RouteSpecificPool(HttpRoute route, int maxEntries)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      */
getRoute()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      */
getMaxEntries()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      */
isUnused()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      */
getCapacity()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      */
getEntryCount()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      */
allocEntry(final Object state)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      */
freeEntry(BasicPoolEntry entry)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      */
createdEntry(BasicPoolEntry entry)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      */
deleteEntry(BasicPoolEntry entry)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      */
dropEntry()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      */
queueThread(WaitingThread wt)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      */
hasThread()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      */
nextThread()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      */
removeThread(WaitingThread wt)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