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