| /* |
| * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java $ |
| * $Revision: 677240 $ |
| * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $ |
| * |
| * ==================================================================== |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| */ |
| |
| package org.apache.http.impl.conn.tsccm; |
| |
| import java.io.IOException; |
| import java.util.ListIterator; |
| import java.util.Queue; |
| import java.util.LinkedList; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.http.conn.OperatedClientConnection; |
| import org.apache.http.conn.routing.HttpRoute; |
| import org.apache.http.util.LangUtils; |
| |
| |
| /** |
| * A connection sub-pool for a specific route, used by {@link ConnPoolByRoute}. |
| * The methods in this class are unsynchronized. It is expected that the |
| * containing pool takes care of synchronization. |
| */ |
| public class RouteSpecificPool { |
| |
| private final Log log = LogFactory.getLog(getClass()); |
| |
| /** The route this pool is for. */ |
| protected final HttpRoute route; |
| |
| /** the maximum number of entries allowed for this pool */ |
| protected final int maxEntries; |
| |
| /** |
| * The list of free entries. |
| * This list is managed LIFO, to increase idle times and |
| * allow for closing connections that are not really needed. |
| */ |
| protected final LinkedList<BasicPoolEntry> freeEntries; |
| |
| /** The list of threads waiting for this pool. */ |
| protected final Queue<WaitingThread> waitingThreads; |
| |
| /** The number of created entries. */ |
| protected int numEntries; |
| |
| |
| /** |
| * Creates a new route-specific pool. |
| * |
| * @param route the route for which to pool |
| * @param maxEntries the maximum number of entries allowed for this pool |
| */ |
| public RouteSpecificPool(HttpRoute route, int maxEntries) { |
| this.route = route; |
| this.maxEntries = maxEntries; |
| this.freeEntries = new LinkedList<BasicPoolEntry>(); |
| this.waitingThreads = new LinkedList<WaitingThread>(); |
| this.numEntries = 0; |
| } |
| |
| |
| /** |
| * Obtains the route for which this pool is specific. |
| * |
| * @return the route |
| */ |
| public final HttpRoute getRoute() { |
| return route; |
| } |
| |
| |
| /** |
| * Obtains the maximum number of entries allowed for this pool. |
| * |
| * @return the max entry number |
| */ |
| public final int getMaxEntries() { |
| return maxEntries; |
| } |
| |
| |
| /** |
| * Indicates whether this pool is unused. |
| * A pool is unused if there is neither an entry nor a waiting thread. |
| * All entries count, not only the free but also the allocated ones. |
| * |
| * @return <code>true</code> if this pool is unused, |
| * <code>false</code> otherwise |
| */ |
| public boolean isUnused() { |
| return (numEntries < 1) && waitingThreads.isEmpty(); |
| } |
| |
| |
| /** |
| * Return remaining capacity of this pool |
| * |
| * @return capacity |
| */ |
| public int getCapacity() { |
| return maxEntries - numEntries; |
| } |
| |
| |
| /** |
| * Obtains the number of entries. |
| * This includes not only the free entries, but also those that |
| * have been created and are currently issued to an application. |
| * |
| * @return the number of entries for the route of this pool |
| */ |
| public final int getEntryCount() { |
| return numEntries; |
| } |
| |
| |
| /** |
| * Obtains a free entry from this pool, if one is available. |
| * |
| * @return an available pool entry, or <code>null</code> if there is none |
| */ |
| public BasicPoolEntry allocEntry(final Object state) { |
| if (!freeEntries.isEmpty()) { |
| ListIterator<BasicPoolEntry> it = freeEntries.listIterator(freeEntries.size()); |
| while (it.hasPrevious()) { |
| BasicPoolEntry entry = it.previous(); |
| if (LangUtils.equals(state, entry.getState())) { |
| it.remove(); |
| return entry; |
| } |
| } |
| } |
| if (!freeEntries.isEmpty()) { |
| BasicPoolEntry entry = freeEntries.remove(); |
| entry.setState(null); |
| OperatedClientConnection conn = entry.getConnection(); |
| try { |
| conn.close(); |
| } catch (IOException ex) { |
| log.debug("I/O error closing connection", ex); |
| } |
| return entry; |
| } |
| return null; |
| } |
| |
| |
| /** |
| * Returns an allocated entry to this pool. |
| * |
| * @param entry the entry obtained from {@link #allocEntry allocEntry} |
| * or presented to {@link #createdEntry createdEntry} |
| */ |
| public void freeEntry(BasicPoolEntry entry) { |
| |
| if (numEntries < 1) { |
| throw new IllegalStateException |
| ("No entry created for this pool. " + route); |
| } |
| if (numEntries <= freeEntries.size()) { |
| throw new IllegalStateException |
| ("No entry allocated from this pool. " + route); |
| } |
| freeEntries.add(entry); |
| } |
| |
| |
| /** |
| * Indicates creation of an entry for this pool. |
| * The entry will <i>not</i> be added to the list of free entries, |
| * it is only recognized as belonging to this pool now. It can then |
| * be passed to {@link #freeEntry freeEntry}. |
| * |
| * @param entry the entry that was created for this pool |
| */ |
| public void createdEntry(BasicPoolEntry entry) { |
| |
| if (!route.equals(entry.getPlannedRoute())) { |
| throw new IllegalArgumentException |
| ("Entry not planned for this pool." + |
| "\npool: " + route + |
| "\nplan: " + entry.getPlannedRoute()); |
| } |
| |
| numEntries++; |
| } |
| |
| |
| /** |
| * Deletes an entry from this pool. |
| * Only entries that are currently free in this pool can be deleted. |
| * Allocated entries can not be deleted. |
| * |
| * @param entry the entry to delete from this pool |
| * |
| * @return <code>true</code> if the entry was found and deleted, or |
| * <code>false</code> if the entry was not found |
| */ |
| public boolean deleteEntry(BasicPoolEntry entry) { |
| |
| final boolean found = freeEntries.remove(entry); |
| if (found) |
| numEntries--; |
| return found; |
| } |
| |
| |
| /** |
| * Forgets about an entry from this pool. |
| * This method is used to indicate that an entry |
| * {@link #allocEntry allocated} |
| * from this pool has been lost and will not be returned. |
| */ |
| public void dropEntry() { |
| if (numEntries < 1) { |
| throw new IllegalStateException |
| ("There is no entry that could be dropped."); |
| } |
| numEntries--; |
| } |
| |
| |
| /** |
| * Adds a waiting thread. |
| * This pool makes no attempt to match waiting threads with pool entries. |
| * It is the caller's responsibility to check that there is no entry |
| * before adding a waiting thread. |
| * |
| * @param wt the waiting thread |
| */ |
| public void queueThread(WaitingThread wt) { |
| if (wt == null) { |
| throw new IllegalArgumentException |
| ("Waiting thread must not be null."); |
| } |
| this.waitingThreads.add(wt); |
| } |
| |
| |
| /** |
| * Checks whether there is a waiting thread in this pool. |
| * |
| * @return <code>true</code> if there is a waiting thread, |
| * <code>false</code> otherwise |
| */ |
| public boolean hasThread() { |
| return !this.waitingThreads.isEmpty(); |
| } |
| |
| |
| /** |
| * Returns the next thread in the queue. |
| * |
| * @return a waiting thread, or <code>null</code> if there is none |
| */ |
| public WaitingThread nextThread() { |
| return this.waitingThreads.peek(); |
| } |
| |
| |
| /** |
| * Removes a waiting thread, if it is queued. |
| * |
| * @param wt the waiting thread |
| */ |
| public void removeThread(WaitingThread wt) { |
| if (wt == null) |
| return; |
| |
| this.waitingThreads.remove(wt); |
| } |
| |
| |
| } // class RouteSpecificPool |