You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2011/07/28 13:42:25 UTC
svn commit: r1151800 - in /httpcomponents/httpcore/trunk/httpcore/src:
main/java/org/apache/http/pool/ test/java/org/apache/http/pool/
Author: olegk
Date: Thu Jul 28 11:42:23 2011
New Revision: 1151800
URL: http://svn.apache.org/viewvc?rev=1151800&view=rev
Log:
Migrated connection pooling code from HttpClient to HttpCore with some classes borrowed from HttpAsyncClient
Added:
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java (with props)
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java (with props)
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntry.java
- copied, changed from r1151791, httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java (with props)
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolStats.java
- copied, changed from r1151791, httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/nio/conn/PoolStats.java
httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java (with props)
httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/
httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java (with props)
httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java (with props)
Added: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java?rev=1151800&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java (added)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java Thu Jul 28 11:42:23 2011
@@ -0,0 +1,407 @@
+/*
+ * ====================================================================
+ * 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.pool;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>> implements ConnPoolControl<T> {
+
+ private final Map<T, RouteSpecificPool<T, C, E>> routeToPool;
+ private final Set<E> leased;
+ private final LinkedList<E> available;
+ private final LinkedList<PoolEntryFuture<E>> pending;
+ private final Map<T, Integer> maxPerRoute;
+ private final Lock lock;
+
+ private volatile boolean isShutDown;
+ private volatile int defaultMaxPerRoute;
+ private volatile int maxTotal;
+
+ public AbstractConnPool(
+ int defaultMaxPerRoute,
+ int maxTotal) {
+ super();
+ if (defaultMaxPerRoute <= 0) {
+ throw new IllegalArgumentException("Max per route value may not be negative or zero");
+ }
+ if (maxTotal <= 0) {
+ throw new IllegalArgumentException("Max total value may not be negative or zero");
+ }
+ this.routeToPool = new HashMap<T, RouteSpecificPool<T, C, E>>();
+ this.leased = new HashSet<E>();
+ this.available = new LinkedList<E>();
+ this.pending = new LinkedList<PoolEntryFuture<E>>();
+ this.maxPerRoute = new HashMap<T, Integer>();
+ this.lock = new ReentrantLock();
+ this.defaultMaxPerRoute = defaultMaxPerRoute;
+ this.maxTotal = maxTotal;
+ }
+
+ protected abstract C createConnection(T route) throws IOException;
+
+ protected abstract E createEntry(T route, C conn);
+
+ protected abstract void closeEntry(E entry);
+
+ public boolean isShutdown() {
+ return this.isShutDown;
+ }
+
+ public void shutdown(long waitMs) throws IOException {
+ if (this.isShutDown) {
+ return ;
+ }
+ this.isShutDown = true;
+ this.lock.lock();
+ try {
+ for (E entry: this.available) {
+ closeEntry(entry);
+ }
+ for (E entry: this.leased) {
+ closeEntry(entry);
+ }
+ for (RouteSpecificPool<T, C, E> pool: this.routeToPool.values()) {
+ pool.shutdown();
+ }
+ this.routeToPool.clear();
+ this.leased.clear();
+ this.available.clear();
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ private RouteSpecificPool<T, C, E> getPool(final T route) {
+ RouteSpecificPool<T, C, E> pool = this.routeToPool.get(route);
+ if (pool == null) {
+ pool = new RouteSpecificPool<T, C, E>(route) {
+
+ @Override
+ protected E createEntry(C conn) {
+ return AbstractConnPool.this.createEntry(route, conn);
+ }
+
+ @Override
+ protected void closeEntry(final E entry) {
+ AbstractConnPool.this.closeEntry(entry);
+ }
+
+ };
+ this.routeToPool.put(route, pool);
+ }
+ return pool;
+ }
+
+ public Future<E> lease(final T route, final Object state) {
+ if (route == null) {
+ throw new IllegalArgumentException("Route may not be null");
+ }
+ if (this.isShutDown) {
+ throw new IllegalStateException("Connection pool shut down");
+ }
+ return new PoolEntryFuture<E>(this.lock) {
+
+ @Override
+ public E getPoolEntry(
+ long timeout,
+ TimeUnit tunit)
+ throws InterruptedException, TimeoutException, IOException {
+ return getPoolEntryBlocking(route, state, timeout, tunit, this);
+ }
+
+ };
+ }
+
+ private E getPoolEntryBlocking(
+ final T route, final Object state,
+ final long timeout, final TimeUnit tunit,
+ final PoolEntryFuture<E> future)
+ throws IOException, InterruptedException, TimeoutException {
+
+ Date deadline = null;
+ if (timeout > 0) {
+ deadline = new Date
+ (System.currentTimeMillis() + tunit.toMillis(timeout));
+ }
+
+ this.lock.lock();
+ try {
+ RouteSpecificPool<T, C, E> pool = getPool(route);
+ E entry = null;
+ while (entry == null) {
+ if (this.isShutDown) {
+ throw new IllegalStateException("Connection pool shut down");
+ }
+ for (;;) {
+ entry = pool.getFree(state);
+ if (entry == null) {
+ break;
+ }
+ if (entry.isExpired(System.currentTimeMillis())) {
+ closeEntry(entry);
+ this.available.remove(entry);
+ pool.free(entry, false);
+ } else {
+ break;
+ }
+ }
+ if (entry != null) {
+ this.available.remove(entry);
+ this.leased.add(entry);
+ return entry;
+ }
+
+ if (pool.getAllocatedCount() < getMaxPerRoute(route)) {
+ int totalUsed = this.leased.size();
+ int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
+ if (freeCapacity > 0) {
+ int totalAvailable = this.available.size();
+ if (totalAvailable > freeCapacity - 1) {
+ dropLastUsed();
+ }
+ C conn = createConnection(route);
+ entry = pool.add(conn);
+ this.leased.add(entry);
+ return entry;
+ }
+ }
+
+ boolean success = false;
+ try {
+ pool.queue(future);
+ this.pending.add(future);
+ success = future.await(deadline);
+ } finally {
+ // In case of 'success', we were woken up by the
+ // connection pool and should now have a connection
+ // waiting for us, or else we're shutting down.
+ // Just continue in the loop, both cases are checked.
+ pool.unqueue(future);
+ this.pending.remove(future);
+ }
+ // check for spurious wakeup vs. timeout
+ if (!success && (deadline != null) &&
+ (deadline.getTime() <= System.currentTimeMillis())) {
+ break;
+ }
+ }
+ throw new TimeoutException("Timeout waiting for connection");
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ private void notifyPending(final RouteSpecificPool<T, C, E> pool) {
+ PoolEntryFuture<E> future = pool.nextPending();
+ if (future != null) {
+ this.pending.remove(future);
+ } else {
+ future = this.pending.poll();
+ }
+ if (future != null) {
+ future.wakeup();
+ }
+ }
+
+ public void release(E entry, boolean reusable) {
+ this.lock.lock();
+ try {
+ if (this.leased.remove(entry)) {
+ RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
+ pool.free(entry, reusable);
+ if (reusable && !this.isShutDown) {
+ this.available.add(entry);
+ } else {
+ closeEntry(entry);
+ }
+ notifyPending(pool);
+ }
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ private void dropLastUsed() {
+ if (!this.available.isEmpty()) {
+ E entry = this.available.removeFirst();
+ closeEntry(entry);
+ RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
+ pool.remove(entry);
+ }
+ }
+
+ private int getMaxPerRoute(final T route) {
+ Integer v = this.maxPerRoute.get(route);
+ if (v != null) {
+ return v.intValue();
+ } else {
+ return this.defaultMaxPerRoute;
+ }
+ }
+
+ public void setTotalMax(int max) {
+ if (max <= 0) {
+ throw new IllegalArgumentException("Max value may not be negative or zero");
+ }
+ this.lock.lock();
+ try {
+ this.maxTotal = max;
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public void setDefaultMaxPerHost(int max) {
+ if (max <= 0) {
+ throw new IllegalArgumentException("Max value may not be negative or zero");
+ }
+ this.lock.lock();
+ try {
+ this.defaultMaxPerRoute = max;
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public void setMaxPerHost(final T route, int max) {
+ if (route == null) {
+ throw new IllegalArgumentException("Route may not be null");
+ }
+ if (max <= 0) {
+ throw new IllegalArgumentException("Max value may not be negative or zero");
+ }
+ this.lock.lock();
+ try {
+ this.maxPerRoute.put(route, max);
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public PoolStats getTotalStats() {
+ this.lock.lock();
+ try {
+ return new PoolStats(
+ this.leased.size(),
+ this.pending.size(),
+ this.available.size(),
+ this.maxTotal);
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public PoolStats getStats(final T route) {
+ if (route == null) {
+ throw new IllegalArgumentException("Route may not be null");
+ }
+ this.lock.lock();
+ try {
+ RouteSpecificPool<T, C, E> pool = getPool(route);
+ return new PoolStats(
+ pool.getLeasedCount(),
+ pool.getPendingCount(),
+ pool.getAvailableCount(),
+ getMaxPerRoute(route));
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public void closeIdle(long idletime, final TimeUnit tunit) {
+ if (tunit == null) {
+ throw new IllegalArgumentException("Time unit must not be null.");
+ }
+ long time = tunit.toMillis(idletime);
+ if (time < 0) {
+ time = 0;
+ }
+ long deadline = System.currentTimeMillis() - time;
+ this.lock.lock();
+ try {
+ Iterator<E> it = this.available.iterator();
+ while (it.hasNext()) {
+ E entry = it.next();
+ if (entry.getUpdated() <= deadline) {
+ closeEntry(entry);
+ RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
+ pool.remove(entry);
+ it.remove();
+ notifyPending(pool);
+ }
+ }
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public void closeExpired() {
+ long now = System.currentTimeMillis();
+ this.lock.lock();
+ try {
+ Iterator<E> it = this.available.iterator();
+ while (it.hasNext()) {
+ E entry = it.next();
+ if (entry.isExpired(now)) {
+ closeEntry(entry);
+ RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
+ pool.remove(entry);
+ it.remove();
+ notifyPending(pool);
+ }
+ }
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("[leased: ");
+ buffer.append(this.leased);
+ buffer.append("][available: ");
+ buffer.append(this.available);
+ buffer.append("][pending: ");
+ buffer.append(this.pending);
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/AbstractConnPool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java?rev=1151800&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java (added)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java Thu Jul 28 11:42:23 2011
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * 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.pool;
+
+public interface ConnPoolControl<T> {
+
+ void setTotalMax(int max);
+
+ void setDefaultMaxPerHost(int max);
+
+ void setMaxPerHost(final T route, int max);
+
+ PoolStats getTotalStats();
+
+ PoolStats getStats(final T route);
+
+}
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/ConnPoolControl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Copied: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntry.java (from r1151791, httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntry.java?p2=httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntry.java&p1=httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java&r1=1151791&r2=1151800&rev=1151800&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/pool/PoolEntry.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntry.java Thu Jul 28 11:42:23 2011
@@ -24,20 +24,18 @@
* <http://www.apache.org/>.
*
*/
-package org.apache.http.impl.nio.pool;
+package org.apache.http.pool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
-import org.apache.http.nio.reactor.IOSession;
-
-public class PoolEntry<T> {
+public class PoolEntry<T, C> {
private static AtomicLong COUNTER = new AtomicLong();
private final long id;
private final T route;
- private final IOSession session;
+ private final C conn;
private final long created;
private final long validUnit;
@@ -45,20 +43,20 @@ public class PoolEntry<T> {
private long updated;
private long expiry;
- public PoolEntry(final T route, final IOSession session,
+ public PoolEntry(final T route, final C conn,
final long timeToLive, final TimeUnit tunit) {
super();
if (route == null) {
throw new IllegalArgumentException("Route may not be null");
}
- if (session == null) {
- throw new IllegalArgumentException("I/O session may not be null");
+ if (conn == null) {
+ throw new IllegalArgumentException("Connection may not be null");
}
if (tunit == null) {
throw new IllegalArgumentException("Time unit may not be null");
}
this.route = route;
- this.session = session;
+ this.conn = conn;
this.id = COUNTER.incrementAndGet();
this.created = System.currentTimeMillis();
if (timeToLive > 0) {
@@ -69,12 +67,16 @@ public class PoolEntry<T> {
this.expiry = this.validUnit;
}
+ public PoolEntry(final T route, final C conn) {
+ this(route, conn, 0, TimeUnit.MILLISECONDS);
+ }
+
protected T getRoute() {
return this.route;
}
- protected IOSession getIOSession() {
- return this.session;
+ protected C getConnection() {
+ return this.conn;
}
public long getCreated() {
Added: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java?rev=1151800&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java (added)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java Thu Jul 28 11:42:23 2011
@@ -0,0 +1,138 @@
+/*
+ * ====================================================================
+ *
+ * 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.pool;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+abstract class PoolEntryFuture<T> implements Future<T> {
+
+ private final Lock lock;
+ private final Condition condition;
+ private volatile boolean cancelled;
+ private volatile boolean completed;
+ private T result;
+
+ PoolEntryFuture(final Lock lock) {
+ super();
+ this.lock = lock;
+ this.condition = lock.newCondition();
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ this.lock.lock();
+ try {
+ if (this.completed) {
+ return false;
+ }
+ this.completed = true;
+ this.cancelled = true;
+ this.condition.signalAll();
+ return true;
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ public boolean isDone() {
+ return this.completed;
+ }
+
+ public T get() throws InterruptedException, ExecutionException {
+ try {
+ return get(0, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException ex) {
+ throw new ExecutionException(ex);
+ }
+ }
+
+ public T get(
+ long timeout,
+ final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ this.lock.lock();
+ try {
+ if (this.completed) {
+ return this.result;
+ }
+ this.result = getPoolEntry(timeout, unit);
+ this.completed = true;
+ return result;
+ } catch (IOException ex) {
+ this.completed = true;
+ this.result = null;
+ throw new ExecutionException(ex);
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+ protected abstract T getPoolEntry(
+ long timeout, TimeUnit unit) throws IOException, InterruptedException, TimeoutException;
+
+ public boolean await(final Date deadline) throws InterruptedException {
+ this.lock.lock();
+ try {
+ if (this.cancelled) {
+ throw new InterruptedException("Operation interrupted");
+ }
+ boolean success = false;
+ if (deadline != null) {
+ success = this.condition.awaitUntil(deadline);
+ } else {
+ this.condition.await();
+ success = true;
+ }
+ if (this.cancelled) {
+ throw new InterruptedException("Operation interrupted");
+ }
+ return success;
+ } finally {
+ this.lock.unlock();
+ }
+
+ }
+
+ public void wakeup() {
+ this.lock.lock();
+ try {
+ this.condition.signalAll();
+ } finally {
+ this.lock.unlock();
+ }
+ }
+
+}
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolEntryFuture.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Copied: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolStats.java (from r1151791, httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/nio/conn/PoolStats.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolStats.java?p2=httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolStats.java&p1=httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/nio/conn/PoolStats.java&r1=1151791&r2=1151800&rev=1151800&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/nio/conn/PoolStats.java (original)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/PoolStats.java Thu Jul 28 11:42:23 2011
@@ -24,7 +24,7 @@
* <http://www.apache.org/>.
*
*/
-package org.apache.http.nio.conn;
+package org.apache.http.pool;
public class PoolStats {
Added: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java?rev=1151800&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java (added)
+++ httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java Thu Jul 28 11:42:23 2011
@@ -0,0 +1,182 @@
+/*
+ * ====================================================================
+ *
+ * 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.pool;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+
+import org.apache.http.annotation.NotThreadSafe;
+
+@NotThreadSafe
+abstract class RouteSpecificPool<T, C, E extends PoolEntry<T, C>> {
+
+ private final T route;
+ private final Set<E> leased;
+ private final LinkedList<E> available;
+ private final LinkedList<PoolEntryFuture<E>> pending;
+
+ RouteSpecificPool(final T route) {
+ super();
+ this.route = route;
+ this.leased = new HashSet<E>();
+ this.available = new LinkedList<E>();
+ this.pending = new LinkedList<PoolEntryFuture<E>>();
+ }
+
+ protected abstract E createEntry(C conn);
+
+ protected abstract void closeEntry(E entry);
+
+ public final T getRoute() {
+ return route;
+ }
+
+ public int getLeasedCount() {
+ return this.leased.size();
+ }
+
+ public int getPendingCount() {
+ return this.pending.size();
+ }
+
+ public int getAvailableCount() {
+ return this.available.size();
+ }
+
+ public int getAllocatedCount() {
+ return this.available.size() + this.leased.size();
+ }
+
+ public E getFree(final Object state) {
+ if (!this.available.isEmpty()) {
+ if (state != null) {
+ Iterator<E> it = this.available.iterator();
+ while (it.hasNext()) {
+ E entry = it.next();
+ if (state.equals(entry.getState())) {
+ it.remove();
+ this.leased.add(entry);
+ return entry;
+ }
+ }
+ }
+ Iterator<E> it = this.available.iterator();
+ while (it.hasNext()) {
+ E entry = it.next();
+ if (entry.getState() == null) {
+ it.remove();
+ this.leased.add(entry);
+ return entry;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean remove(final E entry) {
+ if (entry == null) {
+ throw new IllegalArgumentException("Pool entry may not be null");
+ }
+ if (!this.available.remove(entry)) {
+ if (!this.leased.remove(entry)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void free(final E entry, boolean reusable) {
+ if (entry == null) {
+ throw new IllegalArgumentException("Pool entry may not be null");
+ }
+ boolean found = this.leased.remove(entry);
+ if (!found) {
+ throw new IllegalStateException("Entry " + entry +
+ " has not been leased from this pool");
+ }
+ if (reusable) {
+ this.available.add(entry);
+ }
+ }
+
+ public E add(final C conn) {
+ E entry = createEntry(conn);
+ this.leased.add(entry);
+ return entry;
+ }
+
+ public void queue(final PoolEntryFuture<E> future) {
+ if (future == null) {
+ return;
+ }
+ this.pending.add(future);
+ }
+
+ public PoolEntryFuture<E> nextPending() {
+ return this.pending.poll();
+ }
+
+ public void unqueue(final PoolEntryFuture<E> future) {
+ if (future == null)
+ return;
+
+ this.pending.remove(future);
+ }
+
+ public void shutdown() {
+ for (PoolEntryFuture<E> future: this.pending) {
+ future.cancel(true);
+ }
+ this.pending.clear();
+ for (E entry: this.available) {
+ closeEntry(entry);
+ }
+ this.available.clear();
+ for (E entry: this.leased) {
+ closeEntry(entry);
+ }
+ this.leased.clear();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("[route: ");
+ buffer.append(this.route);
+ buffer.append("][leased: ");
+ buffer.append(this.leased.size());
+ buffer.append("][available: ");
+ buffer.append(this.available.size());
+ buffer.append("][pending: ");
+ buffer.append(this.pending.size());
+ buffer.append("]");
+ return buffer.toString();
+ }
+
+}
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore/src/main/java/org/apache/http/pool/RouteSpecificPool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java?rev=1151800&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java (added)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java Thu Jul 28 11:42:23 2011
@@ -0,0 +1,729 @@
+/*
+ * ====================================================================
+ * 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.pool;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Assert;
+
+import org.apache.http.HttpConnection;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestConnPool {
+
+ private static final int GRACE_PERIOD = 10000;
+
+ interface HttpConnectionFactory {
+
+ HttpConnection create(String route) throws IOException;
+
+ }
+
+ static class LocalPoolEntry extends PoolEntry<String, HttpConnection> {
+
+ public LocalPoolEntry(final String route, final HttpConnection conn) {
+ super(route, conn);
+ }
+
+ }
+
+ static class LocalConnPool extends AbstractConnPool<String, HttpConnection, LocalPoolEntry> {
+
+ private final HttpConnectionFactory connFactory;
+
+ public LocalConnPool(
+ final HttpConnectionFactory connFactory,
+ int defaultMaxPerRoute, int maxTotal) {
+ super(defaultMaxPerRoute, maxTotal);
+ this.connFactory = connFactory;
+ }
+
+ @Override
+ protected HttpConnection createConnection(final String route) throws IOException {
+ return this.connFactory.create(route);
+ }
+
+ @Override
+ protected LocalPoolEntry createEntry(final String route, final HttpConnection conn) {
+ return new LocalPoolEntry(route, conn);
+ }
+
+ @Override
+ protected void closeEntry(final LocalPoolEntry entry) {
+ HttpConnection conn = entry.getConnection();
+ try {
+ conn.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ }
+
+ @Test
+ public void testEmptyPool() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ pool.setDefaultMaxPerHost(5);
+ pool.setMaxPerHost("somehost", 3);
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+ Assert.assertEquals(10, totals.getMax());
+ PoolStats stats = pool.getStats("somehost");
+ Assert.assertEquals(0, stats.getAvailable());
+ Assert.assertEquals(0, stats.getLeased());
+ Assert.assertEquals(0, stats.getPending());
+ Assert.assertEquals(3, stats.getMax());
+ Assert.assertEquals("[leased: []][available: []][pending: []]", pool.toString());
+ }
+
+ @Test
+ public void testInvalidConstruction() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ try {
+ new LocalConnPool(connFactory, -1, 1);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ new LocalConnPool(connFactory, 1, -1);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testLeaseRelease() throws Exception {
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);
+ Mockito.when(connFactory.create(Mockito.eq("otherhost"))).thenReturn(conn2);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry1);
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry2);
+ Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
+ LocalPoolEntry entry3 = future3.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry3);
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(3, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ LocalPoolEntry entry = future1.get();
+ Assert.assertSame(entry1, entry);
+
+ pool.release(entry1, true);
+ pool.release(entry2, true);
+ pool.release(entry3, false);
+ Mockito.verify(conn1, Mockito.never()).close();
+ Mockito.verify(conn2, Mockito.times(1)).close();
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(2, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ }
+
+ @Test
+ public void testLeaseIllegal() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ try {
+ pool.lease(null, null);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testReleaseUnknownEntry() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ pool.release(new LocalPoolEntry("somehost", Mockito.mock(HttpConnection.class)), true);
+ }
+
+ static class GetPoolEntryThread extends Thread {
+
+ private final Future<LocalPoolEntry> future;
+ private final long time;
+ private final TimeUnit tunit;
+
+ private volatile LocalPoolEntry entry;
+ private volatile Exception ex;
+
+ GetPoolEntryThread(final Future<LocalPoolEntry> future, final long time, final TimeUnit tunit) {
+ super();
+ this.future = future;
+ this.time = time;
+ this.tunit = tunit;
+ setDaemon(true);
+ }
+
+ GetPoolEntryThread(final Future<LocalPoolEntry> future) {
+ this(future, 1000, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.entry = this.future.get(this.time, this.tunit);
+ } catch (Exception ex) {
+ this.ex = ex;
+ }
+ }
+
+ public boolean isDone() {
+ return this.future.isDone();
+ }
+
+ public LocalPoolEntry getEntry() {
+ return this.entry;
+ }
+
+ public Exception getException() {
+ return this.ex;
+ }
+
+ }
+
+ @Test
+ public void testMaxLimits() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);
+
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("otherhost"))).thenReturn(conn2);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ pool.setMaxPerHost("somehost", 2);
+ pool.setMaxPerHost("otherhost", 1);
+ pool.setTotalMax(3);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
+ t1.start();
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
+ t2.start();
+ Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
+ GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
+ t3.start();
+
+ t1.join(GRACE_PERIOD);
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = t1.getEntry();
+ Assert.assertNotNull(entry1);
+ t2.join(GRACE_PERIOD);
+ Assert.assertTrue(future2.isDone());
+ LocalPoolEntry entry2 = t2.getEntry();
+ Assert.assertNotNull(entry2);
+ t3.join(GRACE_PERIOD);
+ Assert.assertTrue(future3.isDone());
+ LocalPoolEntry entry3 = t3.getEntry();
+ Assert.assertNotNull(entry3);
+
+ pool.release(entry1, true);
+ pool.release(entry2, true);
+ pool.release(entry3, true);
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(3, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ Future<LocalPoolEntry> future4 = pool.lease("somehost", null);
+ GetPoolEntryThread t4 = new GetPoolEntryThread(future4);
+ t4.start();
+ Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
+ GetPoolEntryThread t5 = new GetPoolEntryThread(future5);
+ t5.start();
+ Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
+ GetPoolEntryThread t6 = new GetPoolEntryThread(future6);
+ t6.start();
+
+ t4.join(GRACE_PERIOD);
+ Assert.assertTrue(future4.isDone());
+ LocalPoolEntry entry4 = t4.getEntry();
+ Assert.assertNotNull(entry4);
+ t5.join(GRACE_PERIOD);
+ Assert.assertTrue(future5.isDone());
+ LocalPoolEntry entry5 = t5.getEntry();
+ Assert.assertNotNull(entry5);
+ t6.join(GRACE_PERIOD);
+ Assert.assertTrue(future6.isDone());
+ LocalPoolEntry entry6 = t6.getEntry();
+ Assert.assertNotNull(entry6);
+
+ Future<LocalPoolEntry> future7 = pool.lease("somehost", null);
+ GetPoolEntryThread t7 = new GetPoolEntryThread(future7);
+ t7.start();
+ Future<LocalPoolEntry> future8 = pool.lease("somehost", null);
+ GetPoolEntryThread t8 = new GetPoolEntryThread(future8);
+ t8.start();
+ Future<LocalPoolEntry> future9 = pool.lease("otherhost", null);
+ GetPoolEntryThread t9 = new GetPoolEntryThread(future9);
+ t9.start();
+
+ Assert.assertFalse(t7.isDone());
+ Assert.assertFalse(t8.isDone());
+ Assert.assertFalse(t9.isDone());
+
+ Mockito.verify(connFactory, Mockito.times(3)).create(Mockito.any(String.class));
+
+ pool.release(entry4, true);
+ pool.release(entry5, false);
+ pool.release(entry6, true);
+
+ t7.join();
+ Assert.assertTrue(future7.isDone());
+ t8.join();
+ Assert.assertTrue(future8.isDone());
+ t9.join();
+ Assert.assertTrue(future9.isDone());
+
+ Mockito.verify(connFactory, Mockito.times(4)).create(Mockito.any(String.class));
+ }
+
+ @Test
+ public void testConnectionRedistributionOnTotalMaxLimit() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1, conn2, conn3);
+
+ HttpConnection conn4 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn5 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("otherhost"))).thenReturn(conn4, conn5);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+ pool.setMaxPerHost("somehost", 2);
+ pool.setMaxPerHost("otherhost", 2);
+ pool.setTotalMax(2);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
+ t1.start();
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
+ t2.start();
+
+ t1.join(GRACE_PERIOD);
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = t1.getEntry();
+ Assert.assertNotNull(entry1);
+ t2.join(GRACE_PERIOD);
+ Assert.assertTrue(future2.isDone());
+ LocalPoolEntry entry2 = t2.getEntry();
+ Assert.assertNotNull(entry2);
+
+ Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
+ GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
+ t3.start();
+ Future<LocalPoolEntry> future4 = pool.lease("otherhost", null);
+ GetPoolEntryThread t4 = new GetPoolEntryThread(future4);
+ t4.start();
+
+ Assert.assertFalse(t3.isDone());
+ Assert.assertFalse(t4.isDone());
+
+ Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));
+ Mockito.verify(connFactory, Mockito.never()).create(Mockito.eq("otherhost"));
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(2, totals.getLeased());
+ Assert.assertEquals(2, totals.getPending());
+
+ pool.release(entry1, true);
+ pool.release(entry2, true);
+
+ t3.join(GRACE_PERIOD);
+ Assert.assertTrue(future3.isDone());
+ LocalPoolEntry entry3 = t3.getEntry();
+ Assert.assertNotNull(entry3);
+ t4.join(GRACE_PERIOD);
+ Assert.assertTrue(future4.isDone());
+ LocalPoolEntry entry4 = t4.getEntry();
+ Assert.assertNotNull(entry4);
+
+ Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));
+ Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("otherhost"));
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(2, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
+ GetPoolEntryThread t5 = new GetPoolEntryThread(future5);
+ t5.start();
+ Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
+ GetPoolEntryThread t6 = new GetPoolEntryThread(future6);
+ t6.start();
+
+ pool.release(entry3, true);
+ pool.release(entry4, true);
+
+ t5.join(GRACE_PERIOD);
+ Assert.assertTrue(future5.isDone());
+ LocalPoolEntry entry5 = t5.getEntry();
+ Assert.assertNotNull(entry5);
+ t6.join(GRACE_PERIOD);
+ Assert.assertTrue(future6.isDone());
+ LocalPoolEntry entry6 = t6.getEntry();
+ Assert.assertNotNull(entry6);
+
+ Mockito.verify(connFactory, Mockito.times(3)).create(Mockito.eq("somehost"));
+ Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("otherhost"));
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(2, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ pool.release(entry5, true);
+ pool.release(entry6, true);
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(2, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+ }
+
+ @Test
+ public void testCreateNewIfExpired() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry1);
+
+ Mockito.verify(connFactory, Mockito.times(1)).create(Mockito.eq("somehost"));
+
+ entry1.updateExpiry(1, TimeUnit.MILLISECONDS);
+ pool.release(entry1, true);
+
+ Thread.sleep(200L);
+
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry2);
+
+ Mockito.verify(connFactory, Mockito.times(2)).create(Mockito.eq("somehost"));
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(1, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+ PoolStats stats = pool.getStats("somehost");
+ Assert.assertEquals(0, stats.getAvailable());
+ Assert.assertEquals(1, stats.getLeased());
+ Assert.assertEquals(0, stats.getPending());
+ }
+
+ @Test
+ public void testCloseExpired() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ Mockito.when(conn1.isOpen()).thenReturn(Boolean.FALSE);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ Mockito.when(conn2.isOpen()).thenReturn(Boolean.TRUE);
+
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1, conn2);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry1);
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry2);
+
+ entry1.updateExpiry(1, TimeUnit.MILLISECONDS);
+ pool.release(entry1, true);
+
+ Thread.sleep(200);
+
+ entry2.updateExpiry(1000, TimeUnit.SECONDS);
+ pool.release(entry2, true);
+
+ pool.closeExpired();
+
+ Mockito.verify(conn1).close();
+ Mockito.verify(conn2, Mockito.never()).close();
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(1, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+ PoolStats stats = pool.getStats("somehost");
+ Assert.assertEquals(1, stats.getAvailable());
+ Assert.assertEquals(0, stats.getLeased());
+ Assert.assertEquals(0, stats.getPending());
+ }
+
+ @Test
+ public void testLeaseTimeout() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 1, 1);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
+ t1.start();
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ GetPoolEntryThread t2 = new GetPoolEntryThread(future2, 50, TimeUnit.MICROSECONDS);
+ t2.start();
+
+ t1.join(GRACE_PERIOD);
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = t1.getEntry();
+ Assert.assertNotNull(entry1);
+ t2.join(GRACE_PERIOD);
+ Assert.assertTrue(t2.getException() instanceof TimeoutException);
+ Assert.assertFalse(future2.isDone());
+ Assert.assertFalse(future2.isCancelled());
+ }
+
+ @Test
+ public void testLeaseIOException() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ Mockito.doThrow(new IOException("Oppsie")).when(connFactory).create("somehost");
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
+ t1.start();
+
+ t1.join(GRACE_PERIOD);
+ Assert.assertTrue(future1.isDone());
+ Assert.assertTrue(t1.getException() instanceof ExecutionException);
+ Assert.assertTrue(t1.getException().getCause() instanceof IOException);
+ Assert.assertFalse(future1.isCancelled());
+ }
+
+ @Test
+ public void testLeaseCacnel() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 1, 1);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
+ t1.start();
+
+ t1.join(GRACE_PERIOD);
+ Assert.assertTrue(future1.isDone());
+ LocalPoolEntry entry1 = t1.getEntry();
+ Assert.assertNotNull(entry1);
+
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
+ t2.start();
+
+ Thread.sleep(5);
+
+ Assert.assertFalse(future2.isDone());
+ Assert.assertFalse(future2.isCancelled());
+
+ future2.cancel(true);
+ t2.join(GRACE_PERIOD);
+ Assert.assertTrue(future2.isDone());
+ Assert.assertTrue(future2.isCancelled());
+ future2.cancel(true);
+ future2.cancel(true);
+ }
+
+ @Test
+ public void testCloseIdle() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1, conn2);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry1);
+ Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
+ LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry2);
+
+ entry1.updateExpiry(0, TimeUnit.MILLISECONDS);
+ pool.release(entry1, true);
+
+ Thread.sleep(200L);
+
+ entry2.updateExpiry(0, TimeUnit.MILLISECONDS);
+ pool.release(entry2, true);
+
+ pool.closeIdle(50, TimeUnit.MILLISECONDS);
+
+ Mockito.verify(conn1).close();
+ Mockito.verify(conn2, Mockito.never()).close();
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(1, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+ PoolStats stats = pool.getStats("somehost");
+ Assert.assertEquals(1, stats.getAvailable());
+ Assert.assertEquals(0, stats.getLeased());
+ Assert.assertEquals(0, stats.getPending());
+
+ pool.closeIdle(-1, TimeUnit.MILLISECONDS);
+
+ Mockito.verify(conn2).close();
+
+ totals = pool.getTotalStats();
+ Assert.assertEquals(0, totals.getAvailable());
+ Assert.assertEquals(0, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+ stats = pool.getStats("somehost");
+ Assert.assertEquals(0, stats.getAvailable());
+ Assert.assertEquals(0, stats.getLeased());
+ Assert.assertEquals(0, stats.getPending());
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testCloseIdleInvalid() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+ pool.closeIdle(50, null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testGetStatsInvalid() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+ pool.getStats(null);
+ }
+
+ @Test
+ public void testSetMaxInvalid() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+ try {
+ pool.setTotalMax(-1);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ pool.setMaxPerHost(null, 1);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ pool.setMaxPerHost("somehost", -1);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ pool.setDefaultMaxPerHost(-1);
+ Assert.fail("IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testShutdown() throws Exception {
+ HttpConnectionFactory connFactory = Mockito.mock(HttpConnectionFactory.class);
+
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("somehost"))).thenReturn(conn1);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ Mockito.when(connFactory.create(Mockito.eq("otherhost"))).thenReturn(conn2);
+
+ LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
+ Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
+ LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry1);
+ Future<LocalPoolEntry> future2 = pool.lease("otherhost", null);
+ LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
+ Assert.assertNotNull(entry2);
+
+ pool.release(entry2, true);
+
+ PoolStats totals = pool.getTotalStats();
+ Assert.assertEquals(1, totals.getAvailable());
+ Assert.assertEquals(1, totals.getLeased());
+ Assert.assertEquals(0, totals.getPending());
+
+ pool.shutdown(1000);
+ Assert.assertTrue(pool.isShutdown());
+ pool.shutdown(1000);
+ pool.shutdown(1000);
+
+ Mockito.verify(conn1, Mockito.atLeastOnce()).close();
+ Mockito.verify(conn2, Mockito.atLeastOnce()).close();
+
+ try {
+ pool.lease("somehost", null);
+ Assert.fail("IllegalStateException should have been thrown");
+ } catch (IllegalStateException expected) {
+ }
+ // Ignored if shut down
+ pool.release(new LocalPoolEntry("somehost", Mockito.mock(HttpConnection.class)), true);
+ }
+
+}
Propchange: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestConnPool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java?rev=1151800&view=auto
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java (added)
+++ httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java Thu Jul 28 11:42:23 2011
@@ -0,0 +1,299 @@
+/*
+ * ====================================================================
+ * 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.pool;
+
+import java.io.IOException;
+
+import junit.framework.Assert;
+
+import org.apache.http.HttpConnection;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestRouteSpecificPool {
+
+ private static final String ROUTE = "whatever";
+
+ static class LocalPoolEntry extends PoolEntry<String, HttpConnection> {
+
+ public LocalPoolEntry(final String route, final HttpConnection conn) {
+ super(route, conn);
+ }
+
+ }
+
+ static class LocalRoutePool extends RouteSpecificPool<String, HttpConnection, LocalPoolEntry> {
+
+ public LocalRoutePool() {
+ super(ROUTE);
+ }
+
+ @Override
+ protected LocalPoolEntry createEntry(final HttpConnection conn) {
+ return new LocalPoolEntry(getRoute(), conn);
+ }
+
+ @Override
+ protected void closeEntry(LocalPoolEntry entry) {
+ HttpConnection conn = entry.getConnection();
+ try {
+ conn.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ };
+
+ @Test
+ public void testEmptyPool() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ Assert.assertEquals(ROUTE, pool.getRoute());
+ Assert.assertEquals(0, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(0, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+ Assert.assertEquals("[route: whatever][leased: 0][available: 0][pending: 0]", pool.toString());
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ HttpConnection conn = Mockito.mock(HttpConnection.class);
+ PoolEntry<String, HttpConnection> entry = pool.add(conn);
+ Assert.assertEquals(1, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(1, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+ Assert.assertNotNull(entry);
+ Assert.assertSame(conn, entry.getConnection());
+ }
+
+ @Test
+ public void testLeaseRelease() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry1 = pool.add(conn1);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry2 = pool.add(conn2);
+ HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry3 = pool.add(conn3);
+
+ Assert.assertNotNull(entry1);
+ Assert.assertNotNull(entry2);
+ Assert.assertNotNull(entry3);
+
+ Assert.assertEquals(3, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(3, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ pool.free(entry1, true);
+ pool.free(entry2, false);
+ pool.free(entry3, true);
+
+ Assert.assertEquals(2, pool.getAllocatedCount());
+ Assert.assertEquals(2, pool.getAvailableCount());
+ Assert.assertEquals(0, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ Assert.assertNotNull(pool.getFree(null));
+ Assert.assertNotNull(pool.getFree(null));
+ Assert.assertNull(pool.getFree(null));
+
+ Assert.assertEquals(2, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(2, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+ }
+
+ @Test
+ public void testLeaseReleaseStateful() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry1 = pool.add(conn1);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry2 = pool.add(conn2);
+ HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry3 = pool.add(conn3);
+
+ Assert.assertNotNull(entry1);
+ Assert.assertNotNull(entry2);
+ Assert.assertNotNull(entry3);
+
+ Assert.assertEquals(3, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(3, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ entry2.setState(Boolean.FALSE);
+ pool.free(entry1, true);
+ pool.free(entry2, true);
+ pool.free(entry3, true);
+
+ Assert.assertEquals(entry2, pool.getFree(Boolean.FALSE));
+ Assert.assertEquals(entry1, pool.getFree(Boolean.FALSE));
+ Assert.assertEquals(entry3, pool.getFree(null));
+ Assert.assertEquals(null, pool.getFree(null));
+
+ entry1.setState(Boolean.TRUE);
+ entry2.setState(Boolean.FALSE);
+ entry3.setState(Boolean.TRUE);
+ pool.free(entry1, true);
+ pool.free(entry2, true);
+ pool.free(entry3, true);
+
+ Assert.assertEquals(null, pool.getFree(null));
+ Assert.assertEquals(entry2, pool.getFree(Boolean.FALSE));
+ Assert.assertEquals(null, pool.getFree(Boolean.FALSE));
+ Assert.assertEquals(entry1, pool.getFree(Boolean.TRUE));
+ Assert.assertEquals(entry3, pool.getFree(Boolean.TRUE));
+ Assert.assertEquals(null, pool.getFree(Boolean.TRUE));
+ }
+
+ @Test(expected=IllegalStateException.class)
+ public void testReleaseInvalidEntry() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ HttpConnection conn = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry = new LocalPoolEntry(ROUTE, conn);
+ pool.free(entry, true);
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry1 = pool.add(conn1);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry2 = pool.add(conn2);
+ HttpConnection conn3 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry3 = pool.add(conn3);
+
+ Assert.assertNotNull(entry1);
+ Assert.assertNotNull(entry2);
+ Assert.assertNotNull(entry3);
+
+ Assert.assertEquals(3, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(3, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ Assert.assertTrue(pool.remove(entry2));
+ Assert.assertFalse(pool.remove(entry2));
+
+ Assert.assertEquals(2, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(2, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ pool.free(entry1, true);
+ pool.free(entry3, true);
+
+ Assert.assertEquals(2, pool.getAllocatedCount());
+ Assert.assertEquals(2, pool.getAvailableCount());
+ Assert.assertEquals(0, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ Assert.assertTrue(pool.remove(entry1));
+ Assert.assertTrue(pool.remove(entry3));
+
+ Assert.assertEquals(0, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(0, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testReleaseInvalid() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ pool.free(null, true);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testRemoveInvalid() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ pool.remove(null);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testWaitingThreadQueuing() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ PoolEntryFuture<LocalPoolEntry> future1 = Mockito.mock(PoolEntryFuture.class);
+ PoolEntryFuture<LocalPoolEntry> future2 = Mockito.mock(PoolEntryFuture.class);
+
+ Assert.assertEquals(0, pool.getPendingCount());
+ pool.queue(future1);
+ Assert.assertEquals(1, pool.getPendingCount());
+ pool.queue(null);
+ Assert.assertEquals(1, pool.getPendingCount());
+ pool.queue(future2);
+ Assert.assertEquals(2, pool.getPendingCount());
+ Assert.assertSame(future1, pool.nextPending());
+ pool.unqueue(future1);
+ Assert.assertEquals(1, pool.getPendingCount());
+ Assert.assertSame(future2, pool.nextPending());
+ pool.unqueue(null);
+ Assert.assertEquals(0, pool.getPendingCount());
+ pool.unqueue(future2);
+ Assert.assertNull(pool.nextPending());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testShutdown() throws Exception {
+ LocalRoutePool pool = new LocalRoutePool();
+ HttpConnection conn1 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry1 = pool.add(conn1);
+ HttpConnection conn2 = Mockito.mock(HttpConnection.class);
+ LocalPoolEntry entry2 = pool.add(conn2);
+
+ PoolEntryFuture<LocalPoolEntry> future1 = Mockito.mock(PoolEntryFuture.class);
+ pool.queue(future1);
+
+ Assert.assertNotNull(entry1);
+ Assert.assertNotNull(entry2);
+
+ pool.free(entry1, true);
+
+ Assert.assertEquals(2, pool.getAllocatedCount());
+ Assert.assertEquals(1, pool.getAvailableCount());
+ Assert.assertEquals(1, pool.getLeasedCount());
+ Assert.assertEquals(1, pool.getPendingCount());
+
+ pool.shutdown();
+
+ Assert.assertEquals(0, pool.getAllocatedCount());
+ Assert.assertEquals(0, pool.getAvailableCount());
+ Assert.assertEquals(0, pool.getLeasedCount());
+ Assert.assertEquals(0, pool.getPendingCount());
+
+ Mockito.verify(future1).cancel(true);
+ Mockito.verify(conn2).close();
+ Mockito.verify(conn1).close();
+ }
+
+}
Propchange: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: httpcomponents/httpcore/trunk/httpcore/src/test/java/org/apache/http/pool/TestRouteSpecificPool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain