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 2008/03/19 20:36:06 UTC
svn commit: r638979 - in /httpcomponents/httpclient/trunk: ./
module-client/src/main/java/org/apache/http/conn/
module-client/src/main/java/org/apache/http/impl/client/
module-client/src/main/java/org/apache/http/impl/conn/
module-client/src/main/java/...
Author: olegk
Date: Wed Mar 19 12:36:02 2008
New Revision: 638979
URL: http://svn.apache.org/viewvc?rev=638979&view=rev
Log:
HTTPCLIENT-734: Request abort will unblock the thread waiting for a connection
Contributed by Sam Berlin <sberlin at gmail.com>
Reviewed by Oleg Kalnichevski
Added:
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java (with props)
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java (with props)
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java (with props)
Modified:
httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java
httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java
httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/ExecReqThread.java
httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/GetConnThread.java
httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java
Modified: httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/RELEASE_NOTES.txt?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpclient/trunk/RELEASE_NOTES.txt Wed Mar 19 12:36:02 2008
@@ -1,6 +1,9 @@
Changes since 4.0 Alpha 3
-------------------
+* [HTTPCLIENT-734] Request abort will unblock the thread waiting for a connection
+ Contributed by Sam Berlin <sberlin at gmail.com>
+
* [HTTPCLIENT-759] Ensure release of connections back to the connection manager
on exceptions.
Contributed by Sam Berlin <sberlin at gmail.com>
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionManager.java Wed Mar 19 12:36:02 2008
@@ -117,6 +117,15 @@
TimeUnit tunit)
throws ConnectionPoolTimeoutException, InterruptedException
;
+
+
+ /**
+ * Returns a new {@link ClientConnectionRequest}, from which a
+ * {@link ManagedClientConnection} can be obtained, or the request can be
+ * aborted.
+ */
+ ClientConnectionRequest newConnectionRequest()
+ ;
/**
Added: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java?rev=638979&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java (added)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java Wed Mar 19 12:36:02 2008
@@ -0,0 +1,46 @@
+package org.apache.http.conn;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * Encapsulates a request for a {@link ManagedClientConnection}.
+ */
+public interface ClientConnectionRequest {
+
+ /**
+ * Obtains a connection within a given time.
+ * This method will block until a connection becomes available,
+ * the timeout expires, or the connection manager is
+ * {@link #shutdown shut down}.
+ * Timeouts are handled with millisecond precision.
+ *
+ * If {@link #abortRequest()} is called while this is blocking or
+ * before this began, an {@link InterruptedException} will
+ * be thrown.
+ *
+ * @param route where the connection should point to
+ * @param timeout the timeout, 0 or negative for no timeout
+ * @param tunit the unit for the <code>timeout</code>,
+ * may be <code>null</code> only if there is no timeout
+ *
+ * @return a connection that can be used to communicate
+ * along the given route
+ *
+ * @throws ConnectionPoolTimeoutException
+ * in case of a timeout
+ * @throws InterruptedException
+ * if the calling thread is interrupted while waiting
+ */
+ ManagedClientConnection getConnection(HttpRoute route, long timeout,
+ TimeUnit unit) throws InterruptedException,
+ ConnectionPoolTimeoutException;
+
+ /**
+ * Aborts the call to {@link #getConnection(HttpRoute, long, TimeUnit)},
+ * causing it to throw an {@link InterruptedException}.
+ */
+ void abortRequest();
+
+}
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ClientConnectionRequest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java Wed Mar 19 12:36:02 2008
@@ -47,8 +47,8 @@
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
-import org.apache.http.ProtocolVersion;
import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException;
@@ -69,21 +69,22 @@
import org.apache.http.client.utils.URLUtils;
import org.apache.http.conn.BasicManagedEntity;
import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.Scheme;
+import org.apache.http.conn.routing.BasicRouteDirector;
import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.conn.routing.HttpRouteDirector;
-import org.apache.http.conn.routing.BasicRouteDirector;
-import org.apache.http.conn.Scheme;
-import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
@@ -287,15 +288,20 @@
// request is still available in 'orig'.
HttpRoute route = roureq.getRoute();
+
+ ReleaseTrigger releaseTrigger = new ReleaseTrigger();
+ if (orig instanceof AbortableHttpRequest) {
+ ((AbortableHttpRequest) orig).setReleaseTrigger(releaseTrigger);
+ }
// Allocate connection if needed
if (managedConn == null) {
- managedConn = allocateConnection(route, timeout);
+ ClientConnectionRequest connectionRequest = allocateConnection();
+ releaseTrigger.setClientConnectionRequest(connectionRequest);
+ managedConn = connectionRequest.getConnection(route, timeout, TimeUnit.MILLISECONDS);
}
- if (orig instanceof AbortableHttpRequest) {
- ((AbortableHttpRequest) orig).setReleaseTrigger(managedConn);
- }
+ releaseTrigger.setConnectionReleaseTrigger(managedConn);
// Reopen connection if needed
if (!managedConn.isOpen()) {
@@ -489,23 +495,10 @@
/**
- * Obtains a connection for the target route.
- *
- * @param route the route for which to allocate a connection
- * @param timeout the timeout in milliseconds,
- * 0 or negative for no timeout
- *
- * @throws HttpException in case of a (protocol) problem
- * @throws ConnectionPoolTimeoutException in case of a timeout
- * @throws InterruptedException in case of an interrupt
+ * Obtains a connection request, from which the connection can be retrieved.
*/
- protected ManagedClientConnection allocateConnection(HttpRoute route,
- long timeout)
- throws HttpException, ConnectionPoolTimeoutException,
- InterruptedException {
-
- return connManager.getConnection
- (route, timeout, TimeUnit.MILLISECONDS);
+ protected ClientConnectionRequest allocateConnection() {
+ return connManager.newConnectionRequest();
} // allocateConnection
@@ -1026,5 +1019,70 @@
authState.setAuthScope(authScope);
authState.setCredentials(creds);
}
+
+ /**
+ * A {@link ConnectionReleaseTrigger} that delegates either a
+ * {@link ClientConnectionRequest} or another ConnectionReleaseTrigger
+ * for aborting.
+ */
+ private static class ReleaseTrigger implements ConnectionReleaseTrigger {
+ private boolean aborted = false;
+ private ClientConnectionRequest delegateRequest;
+ private ConnectionReleaseTrigger delegateTrigger;
+
+ void setConnectionReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException {
+ synchronized(this) {
+ if(aborted) {
+ throw new IOException("already aborted!");
+ }
+ this.delegateTrigger = releaseTrigger;
+ this.delegateRequest = null;
+ }
+ }
+
+ void setClientConnectionRequest(ClientConnectionRequest connectionRequest) throws IOException {
+ synchronized(this) {
+ if(aborted) {
+ throw new IOException("already aborted");
+ }
+ this.delegateRequest = connectionRequest;
+ this.delegateTrigger = null;
+ }
+ }
+
+
+ public void abortConnection() throws IOException {
+ ConnectionReleaseTrigger releaseTrigger;
+ ClientConnectionRequest connectionRequest;
+ synchronized(this) {
+ if(aborted)
+ throw new IOException("already aborted");
+ aborted = true;
+ // capture references within lock
+ releaseTrigger = delegateTrigger;
+ connectionRequest = delegateRequest;
+ }
+
+ if(connectionRequest != null)
+ connectionRequest.abortRequest();
+
+ if(releaseTrigger != null) {
+ releaseTrigger.abortConnection();
+ }
+ }
+
+ public void releaseConnection() throws IOException {
+ ConnectionReleaseTrigger releaseTrigger;
+ synchronized(this) {
+ releaseTrigger = delegateTrigger; // capture reference within lock
+ }
+
+ if(releaseTrigger != null)
+ releaseTrigger.releaseConnection();
+ }
+ }
+
+
+
} // class DefaultClientRequestDirector
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java Wed Mar 19 12:36:02 2008
@@ -36,12 +36,13 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.OperatedClientConnection;
import org.apache.http.conn.SchemeRegistry;
+import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.params.HttpParams;
@@ -193,6 +194,22 @@
long timeout,
TimeUnit tunit) {
return getConnection(route);
+ }
+
+ public final ClientConnectionRequest newConnectionRequest() {
+
+ return new ClientConnectionRequest() {
+
+ public void abortRequest() {
+ // Nothing to abort, since requests are immediate.
+ }
+
+ public ManagedClientConnection getConnection(HttpRoute route,
+ long timeout, TimeUnit tunit) {
+ return SingleClientConnManager.this.getConnection(route);
+ }
+
+ };
}
Added: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java?rev=638979&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java (added)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java Wed Mar 19 12:36:02 2008
@@ -0,0 +1,62 @@
+/*
+ * $HeadURL:$
+ * $Revision:$
+ * $Date:$
+ *
+ * ====================================================================
+ *
+ * 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;
+
+/** A simple class that can interrupt a {@link WaitingThread}. */
+public class Aborter {
+
+ private WaitingThread waitingThread;
+ private boolean aborted;
+
+ /**
+ * If a waiting thread has been set, interrupts it.
+ */
+ public void abort() {
+ aborted = true;
+
+ if (waitingThread != null)
+ waitingThread.interrupt();
+
+ }
+
+ /**
+ * Sets the waiting thread. If this has already been aborted,
+ * the waiting thread is immediately interrupted.
+ *
+ * @param waitingThread The thread to interrupt when aborting.
+ */
+ public void setWaitingThread(WaitingThread waitingThread) {
+ this.waitingThread = waitingThread;
+ if (aborted)
+ waitingThread.interrupt();
+ }
+
+}
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/Aborter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java Wed Mar 19 12:36:02 2008
@@ -208,11 +208,18 @@
* @throws InterruptedException
* if the calling thread was interrupted
*/
- public abstract
+ public final
BasicPoolEntry getEntry(HttpRoute route, long timeout, TimeUnit tunit,
ClientConnectionOperator operator)
- throws ConnectionPoolTimeoutException, InterruptedException
- ;
+ throws ConnectionPoolTimeoutException, InterruptedException {
+ return newPoolEntryRequest().getPoolEntry(route, timeout, tunit, operator);
+ }
+
+ /**
+ * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry}
+ * can be obtained, or the request can be aborted.
+ */
+ public abstract PoolEntryRequest newPoolEntryRequest();
/**
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java Wed Mar 19 12:36:02 2008
@@ -205,13 +205,56 @@
poolLock.unlock();
}
}
-
-
- // non-javadoc, see base class AbstractConnPool
+
@Override
- public BasicPoolEntry getEntry(HttpRoute route,
+ public PoolEntryRequest newPoolEntryRequest() {
+
+ final Aborter aborter = new Aborter();
+
+ return new PoolEntryRequest() {
+
+ public void abortRequest() {
+ try {
+ poolLock.lock();
+ aborter.abort();
+ } finally {
+ poolLock.unlock();
+ }
+ }
+
+ public BasicPoolEntry getPoolEntry(HttpRoute route, long timeout,
+ TimeUnit tunit, ClientConnectionOperator operator)
+ throws InterruptedException, ConnectionPoolTimeoutException {
+ return getEntryBlocking(route, timeout, tunit, operator, aborter);
+ }
+
+ };
+ }
+
+ /**
+ * Obtains a pool entry with a connection within the given timeout.
+ * If a {@link WaitingThread} is used to block, {@link Aborter#setWaitingThread(WaitingThread)}
+ * must be called before blocking, to allow the thread to be interrupted.
+ *
+ * @param route the route for which to get the connection
+ * @param timeout the timeout, 0 or negative for no timeout
+ * @param tunit the unit for the <code>timeout</code>,
+ * may be <code>null</code> only if there is no timeout
+ * @param operator the connection operator, in case
+ * a connection has to be created
+ * @param aborter an object which can abort a {@link WaitingThread}.
+ *
+ * @return pool entry holding a connection for the route
+ *
+ * @throws ConnectionPoolTimeoutException
+ * if the timeout expired
+ * @throws InterruptedException
+ * if the calling thread was interrupted
+ */
+ protected BasicPoolEntry getEntryBlocking(HttpRoute route,
long timeout, TimeUnit tunit,
- ClientConnectionOperator operator)
+ ClientConnectionOperator operator,
+ Aborter aborter)
throws ConnectionPoolTimeoutException, InterruptedException {
int maxHostConnections = HttpConnectionManagerParams
@@ -269,6 +312,7 @@
if (waitingThread == null) {
waitingThread =
newWaitingThread(poolLock.newCondition(), rospl);
+ aborter.setWaitingThread(waitingThread);
}
boolean success = false;
Added: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java?rev=638979&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java (added)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java Wed Mar 19 12:36:02 2008
@@ -0,0 +1,73 @@
+/*
+ * $HeadURL:$
+ * $Revision:$
+ * $Date:$
+ *
+ * ====================================================================
+ *
+ * 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.util.concurrent.TimeUnit;
+
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * Encapsulates a request for a {@link BasicPoolEntry}.
+ */
+public interface PoolEntryRequest {
+
+ /**
+ * Obtains a pool entry with a connection within the given timeout.
+ * If {@link #abortRequest()} is called before this completes,
+ * an {@link InterruptedException} is thrown.
+ *
+ * @param route the route for which to get the connection
+ * @param timeout the timeout, 0 or negative for no timeout
+ * @param tunit the unit for the <code>timeout</code>,
+ * may be <code>null</code> only if there is no timeout
+ * @param operator the connection operator, in case
+ * a connection has to be created
+ *
+ * @return pool entry holding a connection for the route
+ *
+ * @throws ConnectionPoolTimeoutException
+ * if the timeout expired
+ * @throws InterruptedException
+ * if the calling thread was interrupted
+ */
+ BasicPoolEntry getPoolEntry(HttpRoute route, long timeout, TimeUnit unit,
+ ClientConnectionOperator operator) throws InterruptedException,
+ ConnectionPoolTimeoutException;
+
+ /**
+ * Aborts the active or next call to
+ * {@link #getPoolEntry(HttpRoute, long, TimeUnit, ClientConnectionOperator)}.
+ */
+ void abortRequest();
+
+}
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java Wed Mar 19 12:36:02 2008
@@ -38,6 +38,7 @@
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.OperatedClientConnection;
@@ -147,7 +148,7 @@
// non-javadoc, see interface ClientConnectionManager
- public ManagedClientConnection getConnection(HttpRoute route)
+ public final ManagedClientConnection getConnection(HttpRoute route)
throws InterruptedException {
while (true) {
@@ -166,24 +167,44 @@
// non-javadoc, see interface ClientConnectionManager
- public ManagedClientConnection getConnection(HttpRoute route,
+ public final ManagedClientConnection getConnection(HttpRoute route,
long timeout,
TimeUnit tunit)
throws ConnectionPoolTimeoutException, InterruptedException {
+
+ return newConnectionRequest().getConnection(route, timeout, tunit);
+ }
+
+
+ public ClientConnectionRequest newConnectionRequest() {
+
+ final PoolEntryRequest poolRequest = connectionPool.newPoolEntryRequest();
+
+ return new ClientConnectionRequest() {
+
+ public void abortRequest() {
+ poolRequest.abortRequest();
+ }
+
+ public ManagedClientConnection getConnection(HttpRoute route,
+ long timeout, TimeUnit tunit) throws InterruptedException,
+ ConnectionPoolTimeoutException {
+ if (route == null) {
+ throw new IllegalArgumentException("Route may not be null.");
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ThreadSafeClientConnManager.getConnection: "
+ + route + ", timeout = " + timeout);
+ }
- if (route == null) {
- throw new IllegalArgumentException("Route may not be null.");
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("ThreadSafeClientConnManager.getConnection: "
- + route + ", timeout = " + timeout);
- }
-
- final BasicPoolEntry entry =
- connectionPool.getEntry(route, timeout, tunit, connOperator);
+ final BasicPoolEntry entry = poolRequest.getPoolEntry(route, timeout, tunit, connOperator);
- return new BasicPooledConnAdapter(this, entry);
+ return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry);
+ }
+
+ };
+
}
Modified: httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java Wed Mar 19 12:36:02 2008
@@ -58,6 +58,9 @@
/** The thread that is waiting for an entry. */
private Thread waiter;
+
+ /** True if this was interrupted. */
+ private boolean aborted;
/**
@@ -143,6 +146,9 @@
"\nwaiter: " + this.waiter);
}
+ if (aborted)
+ throw new InterruptedException("interrupted already");
+
this.waiter = Thread.currentThread();
boolean success = false;
@@ -178,6 +184,13 @@
// One condition might be shared by several WaitingThread instances.
// It probably isn't, but just in case: wake all, not just one.
this.cond.signalAll();
+ }
+
+ public void interrupt() {
+ aborted = true;
+
+ if (this.waiter != null)
+ this.waiter.interrupt();
}
Modified: httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java Wed Mar 19 12:36:02 2008
@@ -1,7 +1,7 @@
/*
- * $HeadURL:$
- * $Revision:$
- * $Date:$
+ * $HeadURL$
+ * $Revision$
+ * $Date$
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -30,7 +30,9 @@
import java.io.IOException;
import java.net.ConnectException;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -40,6 +42,7 @@
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.PlainSocketFactory;
@@ -74,6 +77,43 @@
}
/**
+ * Tests that if abort is called on an {@link AbortableHttpRequest} while
+ * {@link DefaultClientRequestDirector} is allocating a connection, that the
+ * connection is properly aborted.
+ */
+ public void testAbortInAllocate() throws Exception {
+ CountDownLatch connLatch = new CountDownLatch(1);
+ CountDownLatch awaitLatch = new CountDownLatch(1);
+ final ConMan conMan = new ConMan(connLatch, awaitLatch);
+ final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
+ final CountDownLatch getLatch = new CountDownLatch(1);
+ final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
+ final HttpContext context = client.getDefaultContext();
+ final HttpGet httpget = new HttpGet("http://www.example.com/a");
+
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ client.execute(httpget, context);
+ } catch(Throwable t) {
+ throwableRef.set(t);
+ } finally {
+ getLatch.countDown();
+ }
+ }
+ }).start();
+
+ assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
+
+ httpget.abort();
+
+ assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
+ assertTrue("should be instanceof InterruptedException, was: " + throwableRef.get(),
+ throwableRef.get() instanceof InterruptedException);
+ }
+
+
+ /**
* Tests that if a socket fails to connect, the allocated connection is
* properly released back to the connection manager.
*/
@@ -160,16 +200,29 @@
}
public ManagedClientConnection getConnection(HttpRoute route,
- long timeout, TimeUnit tunit)
- throws ConnectionPoolTimeoutException, InterruptedException {
- allocatedConnection = new ClientConnAdapterMockup() {
- @Override
- public void open(HttpRoute route, HttpContext context,
- HttpParams params) throws IOException {
- throw new ConnectException();
+ long timeout, TimeUnit tunit) {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+
+ public ClientConnectionRequest newConnectionRequest() {
+ return new ClientConnectionRequest() {
+ public void abortRequest() {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+ public ManagedClientConnection getConnection(HttpRoute route,
+ long timeout, TimeUnit unit)
+ throws InterruptedException,
+ ConnectionPoolTimeoutException {
+ allocatedConnection = new ClientConnAdapterMockup() {
+ @Override
+ public void open(HttpRoute route, HttpContext context,
+ HttpParams params) throws IOException {
+ throw new ConnectException();
+ }
+ };
+ return allocatedConnection;
}
};
- return allocatedConnection;
}
public HttpParams getParams() {
@@ -184,6 +237,73 @@
public void releaseConnection(ManagedClientConnection conn) {
this.releasedConnection = conn;
+ }
+
+ public void shutdown() {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+ }
+
+ private static class ConMan implements ClientConnectionManager {
+ private final CountDownLatch connLatch;
+ private final CountDownLatch awaitLatch;
+
+ public ConMan(CountDownLatch connLatch, CountDownLatch awaitLatch) {
+ this.connLatch = connLatch;
+ this.awaitLatch = awaitLatch;
+ }
+
+ public void closeIdleConnections(long idletime, TimeUnit tunit) {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+
+ public ManagedClientConnection getConnection(HttpRoute route)
+ throws InterruptedException {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+
+ public ManagedClientConnection getConnection(HttpRoute route,
+ long timeout, TimeUnit tunit) {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+
+ public ClientConnectionRequest newConnectionRequest() {
+ final Thread currentThread = Thread.currentThread();
+ return new ClientConnectionRequest() {
+ public void abortRequest() {
+ currentThread.interrupt();
+ }
+
+ public ManagedClientConnection getConnection(HttpRoute route,
+ long timeout, TimeUnit tunit)
+ throws InterruptedException,
+ ConnectionPoolTimeoutException {
+ connLatch.countDown(); // notify waiter that we're getting a connection
+
+ // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
+ if(timeout == 0)
+ timeout = Integer.MAX_VALUE;
+
+ if(!awaitLatch.await(timeout, tunit))
+ throw new ConnectionPoolTimeoutException();
+
+ return new ClientConnAdapterMockup();
+ }
+ };
+ }
+
+ public HttpParams getParams() {
+ throw new UnsupportedOperationException("just a mockup");
+ }
+
+ public SchemeRegistry getSchemeRegistry() {
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(new Scheme("http", new SocketFactoryMockup(null), 80));
+ return registry;
+ }
+
+ public void releaseConnection(ManagedClientConnection conn) {
+ throw new UnsupportedOperationException("just a mockup");
}
public void shutdown() {
Modified: httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/ExecReqThread.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/ExecReqThread.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/ExecReqThread.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/ExecReqThread.java Wed Mar 19 12:36:02 2008
@@ -50,7 +50,8 @@
*/
public class ExecReqThread extends GetConnThread {
- protected RequestSpec request_spec;
+ protected final ClientConnectionManager conn_manager;
+ protected final RequestSpec request_spec;
protected volatile HttpResponse response;
protected volatile byte[] response_data;
@@ -71,6 +72,7 @@
HttpRoute route, long timeout,
RequestSpec reqspec) {
super(mgr, route, timeout);
+ this.conn_manager = mgr;
request_spec = reqspec;
}
Modified: httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/GetConnThread.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/GetConnThread.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/GetConnThread.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/GetConnThread.java Wed Mar 19 12:36:02 2008
@@ -33,6 +33,7 @@
import java.util.concurrent.TimeUnit;
import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.ManagedClientConnection;
@@ -44,34 +45,44 @@
*/
public class GetConnThread extends Thread {
- protected ClientConnectionManager conn_manager;
- protected HttpRoute conn_route;
- protected long conn_timeout;
+ protected final HttpRoute conn_route;
+ protected final long conn_timeout;
+ protected final ClientConnectionRequest conn_request;
protected volatile ManagedClientConnection connection;
protected volatile Throwable exception;
/**
- * Creates a new thread.
+ * Creates a new thread for requesting a connection from the given manager.
+ *
* When this thread is started, it will try to obtain a connection.
* The timeout is in milliseconds.
*/
public GetConnThread(ClientConnectionManager mgr,
HttpRoute route, long timeout) {
-
- conn_manager = mgr;
- conn_route = route;
+ this(mgr.newConnectionRequest(), route, timeout);
+ }
+
+ /**
+ * Creates a new for requesting a connection from the given request object.
+ *
+ * When this thread is started, it will try to obtain a connection.
+ * The timeout is in milliseconds.
+ */
+ public GetConnThread(ClientConnectionRequest connectionRequest,
+ HttpRoute route, long timeout) {
+ conn_route = route;
conn_timeout = timeout;
+ conn_request = connectionRequest;
}
-
/**
* This method is executed when the thread is started.
*/
@Override
public void run() {
try {
- connection = conn_manager.getConnection
+ connection = conn_request.getConnection
(conn_route, conn_timeout, TimeUnit.MILLISECONDS);
} catch (Throwable dart) {
exception = dart;
Modified: httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java?rev=638979&r1=638978&r2=638979&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java (original)
+++ httpcomponents/httpclient/trunk/module-client/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java Wed Mar 19 12:36:02 2008
@@ -38,18 +38,19 @@
import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
+import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.ConnectionPoolTimeoutException;
-import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.PlainSocketFactory;
import org.apache.http.conn.Scheme;
import org.apache.http.conn.SchemeRegistry;
import org.apache.http.conn.SocketFactory;
import org.apache.http.conn.params.HttpConnectionManagerParams;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
/**
@@ -542,6 +543,88 @@
mgr.shutdown();
}
+
+ public void testAbortAfterRequestStarts() throws Exception {
+ HttpParams params = createDefaultParams();
+ HttpConnectionManagerParams.setMaxTotalConnections(params, 1);
+
+ ThreadSafeClientConnManager mgr = createTSCCM(params, null);
+
+ HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+ HttpRoute route = new HttpRoute(target, null, false);
+
+ // get the only connection, then start an extra thread
+ ManagedClientConnection conn = mgr.getConnection(route, 1L, TimeUnit.MILLISECONDS);
+ ClientConnectionRequest request = mgr.newConnectionRequest();
+ GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout
+ gct.start();
+ Thread.sleep(100); // give extra thread time to block
+
+ request.abortRequest();
+
+ gct.join(10000);
+ assertNotNull("thread should have gotten an exception",
+ gct.getException());
+ assertSame("thread got wrong exception",
+ InterruptedException.class,
+ gct.getException().getClass());
+
+ // make sure the manager is still working
+ try {
+ mgr.getConnection(route, 10L, TimeUnit.MILLISECONDS);
+ fail("should have gotten a timeout");
+ } catch (ConnectionPoolTimeoutException e) {
+ // expected
+ }
+
+ mgr.releaseConnection(conn);
+ // this time: no exception
+ conn = mgr.getConnection(route, 10L, TimeUnit.MILLISECONDS);
+ assertNotNull("should have gotten a connection", conn);
+
+ mgr.shutdown();
+ }
+
+ public void testAbortBeforeRequestStarts() throws Exception {
+ HttpParams params = createDefaultParams();
+ HttpConnectionManagerParams.setMaxTotalConnections(params, 1);
+
+ ThreadSafeClientConnManager mgr = createTSCCM(params, null);
+
+ HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+ HttpRoute route = new HttpRoute(target, null, false);
+
+ // get the only connection, then start an extra thread
+ ManagedClientConnection conn = mgr.getConnection(route, 1L, TimeUnit.MILLISECONDS);
+ ClientConnectionRequest request = mgr.newConnectionRequest();
+ request.abortRequest();
+
+ GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout
+ gct.start();
+ Thread.sleep(100); // give extra thread time to block
+
+ gct.join(10000);
+ assertNotNull("thread should have gotten an exception",
+ gct.getException());
+ assertSame("thread got wrong exception",
+ InterruptedException.class,
+ gct.getException().getClass());
+
+ // make sure the manager is still working
+ try {
+ mgr.getConnection(route, 10L, TimeUnit.MILLISECONDS);
+ fail("should have gotten a timeout");
+ } catch (ConnectionPoolTimeoutException e) {
+ // expected
+ }
+
+ mgr.releaseConnection(conn);
+ // this time: no exception
+ conn = mgr.getConnection(route, 10L, TimeUnit.MILLISECONDS);
+ assertNotNull("should have gotten a connection", conn);
+
+ mgr.shutdown();
+ }
} // class TestTSCCMNoServer