From 78856ca1b1a950c677c7a09d139e8f8c7b66e30f Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Sun, 4 Nov 2012 07:37:50 +0000 Subject: [PATCH] Connection management API redesign: deprecated ClientConnectionManager, ManagedClientConnection, OperatedClientConnection and ClientConnectionOperator in favor of a simpler HttpClientConnectionManager interface. The new API has a much smaller footprint and no longer supports the concept of managed or operated connections. Internal connection mangement logic is no longer exposed to the consumer git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1405507 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 1 + .../apache/http/client/fluent/Executor.java | 8 +- .../examples/conn/OperatorConnectDirect.java | 109 ----- .../examples/conn/OperatorConnectProxy.java | 163 ------- .../http/conn/ClientConnectionManager.java | 3 + .../conn/ClientConnectionManagerFactory.java | 4 +- .../http/conn/ClientConnectionOperator.java | 3 + .../http/conn/ClientConnectionRequest.java | 3 + .../apache/http/conn/ConnectionRequest.java | 68 +++ .../conn/HttpClientConnectionManager.java | 125 +++++ .../http/conn/HttpConnectionFactory.java | 40 ++ .../http/conn/HttpRoutedConnection.java | 2 +- .../http/conn/ManagedClientConnection.java | 4 +- .../http/conn/OperatedClientConnection.java | 3 + .../http/conn/scheme/PlainSocketFactory.java | 5 - .../http/impl/client/AbstractHttpClient.java | 3 +- .../http/impl/client/HttpClientBuilder.java | 12 +- .../http/impl/client/InternalHttpClient.java | 42 +- .../impl/client/RequestDirectorAdaptor.java | 126 ----- .../exec/ConnectionReleaseTriggerImpl.java | 134 +++++ .../impl/client/exec/HttpResponseWrapper.java | 29 +- .../http/impl/client/exec/MainClientExec.java | 153 +++--- .../conn/BasicClientConnectionManager.java | 3 + .../BasicHttpClientConnectionManager.java | 335 +++++++++++++ .../java/org/apache/http/impl/conn/CPool.java | 75 +++ .../org/apache/http/impl/conn/CPoolEntry.java | 82 ++++ .../org/apache/http/impl/conn/CPoolProxy.java | 165 +++++++ .../impl/conn/DefaultClientConnection.java | 14 +- .../conn/DefaultClientConnectionFactory.java | 45 ++ .../conn/DefaultClientConnectionOperator.java | 3 + .../conn/HttpClientConnectionManagerBase.java | 215 ++++++++ .../conn/HttpClientConnectionOperator.java | 163 +++++++ .../apache/http/impl/conn/HttpConnPool.java | 3 + .../apache/http/impl/conn/HttpPoolEntry.java | 3 + .../conn/ManagedClientConnectionImpl.java | 6 + .../conn/PoolingClientConnectionManager.java | 3 + .../PoolingHttpClientConnectionManager.java | 231 +++++++++ .../impl/conn/SystemDefaultDnsResolver.java | 2 + .../apache/http/conn/TestConnectionReuse.java | 10 +- .../http/conn/params/TestRouteParams.java | 187 ------- .../impl/client/TestAutoRetryHttpClient.java | 1 + .../impl/client/TestRequestRetryHandler.java | 6 +- .../client/integration/TestAbortHandling.java | 79 +-- .../TestConnectionAutoRelease.java | 42 +- .../TestConnectionManagement.java} | 299 +++--------- .../integration/TestContentCodings.java | 4 +- .../TestIdleConnectionEviction.java | 11 +- .../TestStatefulConnManagement.java | 37 +- .../apache/http/impl/conn/ExecReqThread.java | 120 ----- .../http/impl/conn/TestBasicConnManager.java | 228 --------- .../TestBasicHttpClientConnectionManager.java | 308 ++++++++++++ .../TestDefaultClientConnectOperator.java | 1 + .../TestHttpClientConnectionManagerBase.java | 194 ++++++++ .../TestHttpClientConnectionOperator.java | 179 +++++++ ...stPoolingHttpClientConnectionManager.java} | 185 +++---- .../http/impl/conn/tsccm/AwaitThread.java | 93 ---- .../http/impl/conn/tsccm/GetConnThread.java | 99 ---- .../impl/conn/tsccm/TestConnPoolByRoute.java | 458 ------------------ .../impl/conn/tsccm/TestSpuriousWakeup.java | 167 ------- .../impl/conn/tsccm/TestWaitingThread.java | 172 ------- 60 files changed, 2822 insertions(+), 2446 deletions(-) delete mode 100644 httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectDirect.java delete mode 100644 httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectProxy.java create mode 100644 httpclient/src/main/java/org/apache/http/conn/ConnectionRequest.java create mode 100644 httpclient/src/main/java/org/apache/http/conn/HttpClientConnectionManager.java create mode 100644 httpclient/src/main/java/org/apache/http/conn/HttpConnectionFactory.java delete mode 100644 httpclient/src/main/java/org/apache/http/impl/client/RequestDirectorAdaptor.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/client/exec/ConnectionReleaseTriggerImpl.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/CPool.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/CPoolEntry.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/CPoolProxy.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionFactory.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionOperator.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java delete mode 100644 httpclient/src/test/java/org/apache/http/conn/params/TestRouteParams.java rename httpclient/src/test/java/org/apache/http/impl/{conn/TestPoolingConnManager.java => client/integration/TestConnectionManagement.java} (69%) rename httpclient/src/test/java/org/apache/http/impl/{conn => client/integration}/TestIdleConnectionEviction.java (93%) delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java create mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java create mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java create mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java rename httpclient/src/test/java/org/apache/http/impl/conn/{TestPoolingConnManagerNoServer.java => TestPoolingHttpClientConnectionManager.java} (78%) delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/tsccm/GetConnThread.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java diff --git a/.gitignore b/.gitignore index 535b4cc9a..1b78df0b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +bin .classpath .project .settings diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java index b2cc3a6f0..6cf194417 100644 --- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java @@ -55,18 +55,18 @@ import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.BasicHttpContext; /** * An Executor for fluent requests *

- * A {@link PoolingClientConnectionManager} with maximum 100 connections per route and + * A {@link PoolingHttpClientConnectionManager} with maximum 100 connections per route and * a total maximum of 200 connections is used internally. */ public class Executor { - final static PoolingClientConnectionManager CONNMGR; + final static PoolingHttpClientConnectionManager CONNMGR; final static HttpClient CLIENT; static { @@ -90,7 +90,7 @@ public class Executor { if (ssl != null) { schemeRegistry.register(new Scheme("https", 443, ssl)); } - CONNMGR = new PoolingClientConnectionManager(schemeRegistry); + CONNMGR = new PoolingHttpClientConnectionManager(schemeRegistry); CONNMGR.setDefaultMaxPerRoute(100); CONNMGR.setMaxTotal(200); CLIENT = new HttpClientBuilder().setConnectionManager(CONNMGR).build(); diff --git a/httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectDirect.java b/httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectDirect.java deleted file mode 100644 index bcd8f22b7..000000000 --- a/httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectDirect.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.examples.conn; - -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.HttpVersion; -import org.apache.http.conn.ClientConnectionOperator; -import org.apache.http.conn.OperatedClientConnection; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.impl.conn.DefaultClientConnectionOperator; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.params.SyncBasicHttpParams; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.BasicHttpContext; - -/** - * How to open a direct connection using - * {@link ClientConnectionOperator ClientConnectionOperator}. - * This exemplifies the opening of the connection only. - * The subsequent message exchange in this example should not - * be used as a template. - * - * @since 4.0 - */ -public class OperatorConnectDirect { - - public static void main(String[] args) throws Exception { - HttpHost target = new HttpHost("jakarta.apache.org", 80, "http"); - - // some general setup - // Register the "http" protocol scheme, it is required - // by the default operator to look up socket factories. - SchemeRegistry supportedSchemes = new SchemeRegistry(); - supportedSchemes.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); - - // Prepare parameters. - // Since this example doesn't use the full core framework, - // only few parameters are actually required. - HttpParams params = new SyncBasicHttpParams(); - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - HttpProtocolParams.setUseExpectContinue(params, false); - - // one operator can be used for many connections - ClientConnectionOperator scop = new DefaultClientConnectionOperator(supportedSchemes); - - HttpRequest req = new BasicHttpRequest("OPTIONS", "*", HttpVersion.HTTP_1_1); - req.addHeader("Host", target.getHostName()); - - HttpContext ctx = new BasicHttpContext(); - - OperatedClientConnection conn = scop.createConnection(); - try { - System.out.println("opening connection to " + target); - scop.openConnection(conn, target, null, ctx, params); - System.out.println("sending request"); - conn.sendRequestHeader(req); - // there is no request entity - conn.flush(); - - System.out.println("receiving response header"); - HttpResponse rsp = conn.receiveResponseHeader(); - - System.out.println("----------------------------------------"); - System.out.println(rsp.getStatusLine()); - Header[] headers = rsp.getAllHeaders(); - for (int i = 0; i < headers.length; i++) { - System.out.println(headers[i]); - } - System.out.println("----------------------------------------"); - } finally { - System.out.println("closing connection"); - conn.close(); - } - } - -} - diff --git a/httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectProxy.java b/httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectProxy.java deleted file mode 100644 index c25002bd4..000000000 --- a/httpclient/src/examples/org/apache/http/examples/conn/OperatorConnectProxy.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.examples.conn; - -import org.apache.http.Header; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.HttpVersion; -import org.apache.http.conn.ClientConnectionOperator; -import org.apache.http.conn.OperatedClientConnection; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.conn.DefaultClientConnectionOperator; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.params.SyncBasicHttpParams; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.BasicHttpContext; - -/** - * How to open a secure connection through a proxy using - * {@link ClientConnectionOperator ClientConnectionOperator}. - * This exemplifies the opening of the connection only. - * The message exchange, both subsequently and for tunnelling, - * should not be used as a template. - * - * @since 4.0 - */ -public class OperatorConnectProxy { - - public static void main(String[] args) throws Exception { - - // make sure to use a proxy that supports CONNECT - HttpHost target = new HttpHost("issues.apache.org", 443, "https"); - HttpHost proxy = new HttpHost("127.0.0.1", 8666, "http"); - - // some general setup - // Register the "http" and "https" protocol schemes, they are - // required by the default operator to look up socket factories. - SchemeRegistry supportedSchemes = new SchemeRegistry(); - supportedSchemes.register(new Scheme("http", - 80, PlainSocketFactory.getSocketFactory())); - supportedSchemes.register(new Scheme("https", - 443, SSLSocketFactory.getSocketFactory())); - - // Prepare parameters. - // Since this example doesn't use the full core framework, - // only few parameters are actually required. - HttpParams params = new SyncBasicHttpParams(); - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - HttpProtocolParams.setUseExpectContinue(params, false); - - // one operator can be used for many connections - ClientConnectionOperator scop = new DefaultClientConnectionOperator(supportedSchemes); - - HttpRequest req = new BasicHttpRequest("OPTIONS", "*", HttpVersion.HTTP_1_1); - // In a real application, request interceptors should be used - // to add the required headers. - req.addHeader("Host", target.getHostName()); - - HttpContext ctx = new BasicHttpContext(); - - OperatedClientConnection conn = scop.createConnection(); - try { - System.out.println("opening connection to " + proxy); - scop.openConnection(conn, proxy, null, ctx, params); - - // Creates a request to tunnel a connection. - // For details see RFC 2817, section 5.2 - String authority = target.getHostName() + ":" + target.getPort(); - HttpRequest connect = new BasicHttpRequest("CONNECT", authority, - HttpVersion.HTTP_1_1); - // In a real application, request interceptors should be used - // to add the required headers. - connect.addHeader("Host", authority); - - System.out.println("opening tunnel to " + target); - conn.sendRequestHeader(connect); - // there is no request entity - conn.flush(); - - System.out.println("receiving confirmation for tunnel"); - HttpResponse connected = conn.receiveResponseHeader(); - System.out.println("----------------------------------------"); - printResponseHeader(connected); - System.out.println("----------------------------------------"); - int status = connected.getStatusLine().getStatusCode(); - if ((status < 200) || (status > 299)) { - System.out.println("unexpected status code " + status); - System.exit(1); - } - System.out.println("receiving response body (ignored)"); - conn.receiveResponseEntity(connected); - - // Now we have a tunnel to the target. As we will be creating a - // layered TLS/SSL socket immediately afterwards, updating the - // connection with the new target is optional - but good style. - // The scheme part of the target is already "https", though the - // connection is not yet switched to the TLS/SSL protocol. - conn.update(null, target, false, params); - - System.out.println("layering secure connection"); - scop.updateSecureConnection(conn, target, ctx, params); - - // finally we have the secure connection and can send the request - - System.out.println("sending request"); - conn.sendRequestHeader(req); - // there is no request entity - conn.flush(); - - System.out.println("receiving response header"); - HttpResponse rsp = conn.receiveResponseHeader(); - - System.out.println("----------------------------------------"); - printResponseHeader(rsp); - System.out.println("----------------------------------------"); - - } finally { - System.out.println("closing connection"); - conn.close(); - } - } - - private final static void printResponseHeader(HttpResponse rsp) { - System.out.println(rsp.getStatusLine()); - Header[] headers = rsp.getAllHeaders(); - for (int i=0; i. + * + */ + +package org.apache.http.conn; + +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpClientConnection; +import org.apache.http.concurrent.Cancellable; + +/** + * Encapsulates a request for a {@link HttpClientConnection}. + * + * @since 4.3 + */ +public interface ConnectionRequest extends Cancellable { + + /** + * 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 ClientConnectionManager#shutdown() shut down}. + * Timeouts are handled with millisecond precision. + * + * If {@link #cancel()} is called while this is blocking or + * before this began, an {@link InterruptedException} will + * be thrown. + * + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the timeout, + * may be null 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 + */ + HttpClientConnection get(long timeout, TimeUnit tunit) + throws InterruptedException, ConnectionPoolTimeoutException; + +} diff --git a/httpclient/src/main/java/org/apache/http/conn/HttpClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/conn/HttpClientConnectionManager.java new file mode 100644 index 000000000..e45b793c3 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/conn/HttpClientConnectionManager.java @@ -0,0 +1,125 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.conn; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpHost; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; + +/** + * Represents a manager of persistent client connections. + *

+ * The purpose of an HTTP connection manager is to serve as a factory for new + * HTTP connections, manage persistent connections and synchronize access to + * persistent connections making sure that only one thread of execution can + * have access to a connection at a time. + *

+ * Implementations of this interface must be thread-safe. Access to shared + * data must be synchronized as methods of this interface may be executed + * from multiple threads. + * + * @since 4.3 + */ +public interface HttpClientConnectionManager { + + SchemeRegistry getSchemeRegistry(); + + /** + * Returns a new {@link ClientConnectionRequest}, from which a + * {@link HttpClientConnection} can be obtained or the request can be + * aborted. + */ + ConnectionRequest requestConnection( + HttpRoute route, Object state); + + /** + * Releases a connection for use by others. + * You may optionally specify how long the connection is valid + * to be reused. Values <= 0 are considered to be valid forever. + * If the connection is not marked as reusable, the connection will + * not be reused regardless of the valid duration. + * + * If the connection has been released before, + * the call will be ignored. + * + * @param conn the connection to release + * @param validDuration the duration of time this connection is valid for reuse + * @param timeUnit the unit of time validDuration is measured in + * + * @see #closeExpiredConnections() + */ + void releaseConnection( + HttpClientConnection conn, Object newState, long validDuration, TimeUnit timeUnit); + + void connect( + HttpClientConnection conn, HttpHost host, InetAddress localAddress, + HttpContext context, HttpParams params) throws IOException; + + void upgrade( + HttpClientConnection conn, HttpHost host, + HttpContext context, HttpParams params) throws IOException; + + /** + * Closes idle connections in the pool. + * Open connections in the pool that have not been used for the + * timespan given by the argument will be closed. + * Currently allocated connections are not subject to this method. + * Times will be checked with milliseconds precision + * + * All expired connections will also be closed. + * + * @param idletime the idle time of connections to be closed + * @param tunit the unit for the idletime + * + * @see #closeExpiredConnections() + */ + void closeIdleConnections(long idletime, TimeUnit tunit); + + /** + * Closes all expired connections in the pool. + * Open connections in the pool that have not been used for + * the timespan defined when the connection was released will be closed. + * Currently allocated connections are not subject to this method. + * Times will be checked with milliseconds precision. + */ + void closeExpiredConnections(); + + /** + * Shuts down this connection manager and releases allocated resources. + * This includes closing all connections, whether they are currently + * used or not. + */ + void shutdown(); + +} diff --git a/httpclient/src/main/java/org/apache/http/conn/HttpConnectionFactory.java b/httpclient/src/main/java/org/apache/http/conn/HttpConnectionFactory.java new file mode 100644 index 000000000..b1ff576a4 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/conn/HttpConnectionFactory.java @@ -0,0 +1,40 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.conn; + +import org.apache.http.HttpConnection; + +/** + * Generic {@link HttpConnection} factory. + * + * @since 4.3 + */ +public interface HttpConnectionFactory { + + T create(); + +} diff --git a/httpclient/src/main/java/org/apache/http/conn/HttpRoutedConnection.java b/httpclient/src/main/java/org/apache/http/conn/HttpRoutedConnection.java index a44355577..416f357b9 100644 --- a/httpclient/src/main/java/org/apache/http/conn/HttpRoutedConnection.java +++ b/httpclient/src/main/java/org/apache/http/conn/HttpRoutedConnection.java @@ -37,7 +37,7 @@ import org.apache.http.conn.routing.HttpRoute; * * @since 4.1 * - * @deprecated (4.3) no longer used + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. */ @Deprecated public interface HttpRoutedConnection extends HttpInetConnection { diff --git a/httpclient/src/main/java/org/apache/http/conn/ManagedClientConnection.java b/httpclient/src/main/java/org/apache/http/conn/ManagedClientConnection.java index 270774f76..b5c3e3a1c 100644 --- a/httpclient/src/main/java/org/apache/http/conn/ManagedClientConnection.java +++ b/httpclient/src/main/java/org/apache/http/conn/ManagedClientConnection.java @@ -44,8 +44,10 @@ import org.apache.http.conn.routing.HttpRoute; * Instances are typically obtained from a connection manager. * * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. */ -@SuppressWarnings("deprecation") +@Deprecated public interface ManagedClientConnection extends HttpClientConnection, HttpRoutedConnection, HttpSSLConnection, ConnectionReleaseTrigger { diff --git a/httpclient/src/main/java/org/apache/http/conn/OperatedClientConnection.java b/httpclient/src/main/java/org/apache/http/conn/OperatedClientConnection.java index f7e99bb43..0fa35e3fe 100644 --- a/httpclient/src/main/java/org/apache/http/conn/OperatedClientConnection.java +++ b/httpclient/src/main/java/org/apache/http/conn/OperatedClientConnection.java @@ -41,7 +41,10 @@ import org.apache.http.params.HttpParams; * {@link ClientConnectionOperator operator}. * * @since 4.0 + * + * @deprecated (4.3) replaced by {@link HttpClientConnectionManager}. */ +@Deprecated public interface OperatedClientConnection extends HttpClientConnection, HttpInetConnection { /** diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java index 72f727946..7a9254ead 100644 --- a/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java +++ b/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java @@ -148,11 +148,6 @@ public class PlainSocketFactory implements SocketFactory, SchemeSocketFactory { if (sock == null) { throw new IllegalArgumentException("Socket may not be null."); } - // This check is performed last since it calls a method implemented - // by the argument object. getClass() is final in java.lang.Object. - if (sock.isClosed()) { - throw new IllegalArgumentException("Socket is closed."); - } return false; } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java index 64ee24567..46644310a 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java @@ -940,7 +940,8 @@ public abstract class AbstractHttpClient extends AbstractBasicHttpClient { final AuthenticationStrategy proxyAuthStrategy, final UserTokenHandler userTokenHandler, final HttpParams params) { - return new RequestDirectorAdaptor( + return new DefaultRequestDirector( + log, requestExec, conman, reustrat, diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java index e393f7ef3..5ef09f562 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java @@ -61,8 +61,8 @@ import org.apache.http.client.protocol.RequestClientConnControl; import org.apache.http.client.protocol.RequestDefaultHeaders; import org.apache.http.client.protocol.ResponseContentEncoding; import org.apache.http.client.protocol.ResponseProcessCookies; -import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoutePlanner; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeLayeredSocketFactory; @@ -83,8 +83,8 @@ import org.apache.http.impl.client.exec.ProtocolExec; import org.apache.http.impl.client.exec.RedirectExec; import org.apache.http.impl.client.exec.RetryExec; import org.apache.http.impl.client.exec.ServiceUnavailableRetryExec; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.DefaultHttpRoutePlanner; -import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.impl.conn.ProxySelectorRoutePlanner; import org.apache.http.impl.conn.SchemeRegistryFactory; import org.apache.http.impl.cookie.BestMatchSpecFactory; @@ -185,7 +185,7 @@ public class HttpClientBuilder { private HttpRequestExecutor requestExec; private SchemeLayeredSocketFactory sslSocketFactory; - private ClientConnectionManager connManager; + private HttpClientConnectionManager connManager; private ConnectionReuseStrategy reuseStrategy; private ConnectionKeepAliveStrategy keepAliveStrategy; private AuthenticationStrategy targetAuthStrategy; @@ -240,7 +240,7 @@ public class HttpClientBuilder { return this; } - public final HttpClientBuilder setConnectionManager(final ClientConnectionManager connManager) { + public final HttpClientBuilder setConnectionManager(final HttpClientConnectionManager connManager) { this.connManager = connManager; return this; } @@ -438,7 +438,7 @@ public class HttpClientBuilder { if (requestExec == null) { requestExec = new HttpRequestExecutor(); } - ClientConnectionManager connManager = this.connManager; + HttpClientConnectionManager connManager = this.connManager; if (connManager == null) { SchemeRegistry schemeRegistry = systemProperties ? SchemeRegistryFactory.createSystemDefault() : @@ -446,7 +446,7 @@ public class HttpClientBuilder { if (sslSocketFactory != null) { schemeRegistry.register(new Scheme("https", 443, sslSocketFactory)); } - PoolingClientConnectionManager poolingmgr = new PoolingClientConnectionManager( + PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager( schemeRegistry); if (systemProperties) { String s = System.getProperty("http.keepAlive"); diff --git a/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java index 2a2ab820a..fc3669a92 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/InternalHttpClient.java @@ -28,6 +28,7 @@ package org.apache.http.impl.client; import java.io.IOException; +import java.util.concurrent.TimeUnit; import org.apache.http.HttpException; import org.apache.http.HttpHost; @@ -43,8 +44,12 @@ import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.params.ClientPNames; import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.ClientConnectionRequest; +import org.apache.http.conn.ManagedClientConnection; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRoutePlanner; +import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.cookie.CookieSpecRegistry; import org.apache.http.impl.client.exec.ClientExecChain; import org.apache.http.impl.client.exec.HttpRequestWrapper; @@ -61,7 +66,7 @@ import org.apache.http.protocol.HttpContext; class InternalHttpClient extends AbstractBasicHttpClient { private final ClientExecChain execChain; - private final ClientConnectionManager connManager; + private final HttpClientConnectionManager connManager; private final HttpRoutePlanner routePlanner; private final CookieSpecRegistry cookieSpecRegistry; private final AuthSchemeRegistry authSchemeRegistry; @@ -71,7 +76,7 @@ class InternalHttpClient extends AbstractBasicHttpClient { public InternalHttpClient( final ClientExecChain execChain, - final ClientConnectionManager connManager, + final HttpClientConnectionManager connManager, final HttpRoutePlanner routePlanner, final CookieSpecRegistry cookieSpecRegistry, final AuthSchemeRegistry authSchemeRegistry, @@ -169,7 +174,38 @@ class InternalHttpClient extends AbstractBasicHttpClient { } public ClientConnectionManager getConnectionManager() { - return this.connManager; + + return new ClientConnectionManager() { + + public void shutdown() { + connManager.shutdown(); + } + + public ClientConnectionRequest requestConnection( + HttpRoute route, Object state) { + throw new UnsupportedOperationException(); + } + + public void releaseConnection( + ManagedClientConnection conn, + long validDuration, TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + + public SchemeRegistry getSchemeRegistry() { + throw new UnsupportedOperationException(); + } + + public void closeIdleConnections(long idletime, TimeUnit tunit) { + connManager.closeIdleConnections(idletime, tunit); + } + + public void closeExpiredConnections() { + connManager.closeExpiredConnections(); + } + + }; + } } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/RequestDirectorAdaptor.java b/httpclient/src/main/java/org/apache/http/impl/client/RequestDirectorAdaptor.java deleted file mode 100644 index 4452281e0..000000000 --- a/httpclient/src/main/java/org/apache/http/impl/client/RequestDirectorAdaptor.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.client; - -import java.io.IOException; - -import org.apache.http.ConnectionReuseStrategy; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.annotation.ThreadSafe; -import org.apache.http.auth.AuthState; -import org.apache.http.client.AuthenticationStrategy; -import org.apache.http.client.HttpRequestRetryHandler; -import org.apache.http.client.RedirectStrategy; -import org.apache.http.client.RequestDirector; -import org.apache.http.client.UserTokenHandler; -import org.apache.http.client.methods.HttpExecutionAware; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ConnectionKeepAliveStrategy; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.routing.HttpRoutePlanner; -import org.apache.http.impl.client.exec.ClientExecChain; -import org.apache.http.impl.client.exec.HttpRequestWrapper; -import org.apache.http.impl.client.exec.MainClientExec; -import org.apache.http.impl.client.exec.ProtocolExec; -import org.apache.http.impl.client.exec.RedirectExec; -import org.apache.http.impl.client.exec.RetryExec; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.HttpProcessor; -import org.apache.http.protocol.HttpRequestExecutor; - -@ThreadSafe -class RequestDirectorAdaptor implements RequestDirector { - - private final HttpRoutePlanner routePlanner; - private final HttpParams params; - private final ClientExecChain execChain; - - public RequestDirectorAdaptor( - final HttpRequestExecutor requestExecutor, - final ClientConnectionManager connman, - final ConnectionReuseStrategy reustrat, - final ConnectionKeepAliveStrategy kastrat, - final HttpRoutePlanner rouplan, - final HttpProcessor httpProcessor, - final HttpRequestRetryHandler retryHandler, - final RedirectStrategy redirectStrategy, - final AuthenticationStrategy targetAuthStrategy, - final AuthenticationStrategy proxyAuthStrategy, - final UserTokenHandler userTokenHandler, - final HttpParams params) { - this.routePlanner = rouplan; - this.params = params; - MainClientExec mainExecutor = new MainClientExec( - requestExecutor, connman, reustrat, kastrat, - targetAuthStrategy, proxyAuthStrategy, userTokenHandler); - ProtocolExec protocolFacade = new ProtocolExec(mainExecutor, httpProcessor); - RetryExec retryFacade = new RetryExec(protocolFacade, retryHandler); - RedirectExec redirectFacade = new RedirectExec(retryFacade, rouplan, redirectStrategy); - this.execChain = redirectFacade; - } - - public HttpResponse execute( - HttpHost target, - final HttpRequest request, - final HttpContext context) throws HttpException, IOException { - if (target == null) { - target = (HttpHost) this.params.getParameter(ClientPNames.DEFAULT_HOST); - } - if (target == null) { - throw new IllegalStateException("Target host must not be null, or set in parameters"); - } - HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request); - wrapper.setParams(this.params); - HttpHost virtualHost = (HttpHost) this.params.getParameter(ClientPNames.VIRTUAL_HOST); - wrapper.setVirtualHost(virtualHost); - HttpExecutionAware execListner = null; - if (request instanceof HttpExecutionAware) { - execListner = (HttpExecutionAware) request; - if (execListner.isAborted()) { - throw new RequestAbortedException("Request aborted"); - } - } - HttpRoute route = this.routePlanner.determineRoute(target, request, context); - - if (context.getAttribute(ClientContext.TARGET_AUTH_STATE) == null) { - context.setAttribute(ClientContext.TARGET_AUTH_STATE, new AuthState()); - } - if (context.getAttribute(ClientContext.PROXY_AUTH_STATE) == null) { - context.setAttribute(ClientContext.PROXY_AUTH_STATE, new AuthState()); - } - - return this.execChain.execute(route, wrapper, context, execListner); - } - -} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/exec/ConnectionReleaseTriggerImpl.java b/httpclient/src/main/java/org/apache/http/impl/client/exec/ConnectionReleaseTriggerImpl.java new file mode 100644 index 000000000..80cb251cf --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/client/exec/ConnectionReleaseTriggerImpl.java @@ -0,0 +1,134 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.client.exec; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.http.HttpClientConnection; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.concurrent.Cancellable; +import org.apache.http.conn.ConnectionReleaseTrigger; +import org.apache.http.conn.HttpClientConnectionManager; + +/** + * Internal {@link ConnectionReleaseTrigger} implementation. + * + * @since 4.3 + */ +@ThreadSafe +class ConnectionReleaseTriggerImpl implements ConnectionReleaseTrigger, Cancellable { + + private final Log log; + + private final HttpClientConnectionManager manager; + private final HttpClientConnection managedConn; + private volatile boolean reusable; + private volatile Object state; + private volatile long validDuration; + private volatile TimeUnit tunit; + + private volatile boolean released; + + public ConnectionReleaseTriggerImpl( + final Log log, + final HttpClientConnectionManager manager, + final HttpClientConnection managedConn) { + super(); + this.log = log; + this.manager = manager; + this.managedConn = managedConn; + } + + public boolean isReusable() { + return this.reusable; + } + + public void markReusable() { + this.reusable = true; + } + + public void markNonReusable() { + this.reusable = false; + } + + public void setState(final Object state) { + this.state = state; + } + + public void setValidFor(final long duration, final TimeUnit tunit) { + synchronized (this.managedConn) { + this.validDuration = duration; + this.tunit = tunit; + } + } + + public void releaseConnection() { + synchronized (this.managedConn) { + if (this.released) { + return; + } + this.released = true; + this.manager.releaseConnection(this.managedConn, + this.state, this.validDuration, this.tunit); + } + } + + public void abortConnection() { + synchronized (this.managedConn) { + if (this.released) { + return; + } + this.released = true; + try { + this.managedConn.shutdown(); + log.debug("Connection discarded"); + } catch (IOException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage(), ex); + } + } finally { + this.manager.releaseConnection( + this.managedConn, null, 0, TimeUnit.MILLISECONDS); + } + } + } + + public boolean cancel() { + boolean alreadyReleased = this.released; + log.debug("Cancelling request execution"); + abortConnection(); + return !alreadyReleased; + } + + public boolean isReleased() { + return this.released; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/exec/HttpResponseWrapper.java b/httpclient/src/main/java/org/apache/http/impl/client/exec/HttpResponseWrapper.java index e36e8cb05..f8be43b60 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/exec/HttpResponseWrapper.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/exec/HttpResponseWrapper.java @@ -44,7 +44,6 @@ import org.apache.http.annotation.NotThreadSafe; import org.apache.http.conn.ConnectionReleaseTrigger; import org.apache.http.conn.EofSensorInputStream; import org.apache.http.conn.EofSensorWatcher; -import org.apache.http.conn.ManagedClientConnection; import org.apache.http.entity.HttpEntityWrapper; import org.apache.http.params.HttpParams; import org.apache.http.util.EntityUtils; @@ -59,15 +58,17 @@ import org.apache.http.util.EntityUtils; public class HttpResponseWrapper implements HttpResponse, ConnectionReleaseTrigger, Closeable { private final HttpResponse original; + private final ConnectionReleaseTriggerImpl connReleaseTrigger; private HttpEntity entity; - private ManagedClientConnection conn; - private HttpResponseWrapper(final HttpResponse original, final ManagedClientConnection conn) { + private HttpResponseWrapper( + final HttpResponse original, + final ConnectionReleaseTriggerImpl connReleaseTrigger) { super(); this.original = original; - this.conn = conn; + this.connReleaseTrigger = connReleaseTrigger; HttpEntity entity = original.getEntity(); - if (conn != null && entity != null && entity.isStreaming()) { + if (connReleaseTrigger != null && entity != null && entity.isStreaming()) { this.entity = new EntityWrapper(entity); } } @@ -185,23 +186,21 @@ public class HttpResponseWrapper implements HttpResponse, ConnectionReleaseTrigg } private void cleanup() throws IOException { - if (this.conn != null) { - this.conn.abortConnection(); - this.conn = null; + if (this.connReleaseTrigger != null) { + this.connReleaseTrigger.abortConnection(); } } public void releaseConnection() throws IOException { - if (this.conn != null) { + if (this.connReleaseTrigger != null) { try { - if (this.conn.isMarkedReusable()) { + if (this.connReleaseTrigger.isReusable()) { HttpEntity entity = this.original.getEntity(); if (entity != null) { EntityUtils.consume(entity); } + this.connReleaseTrigger.releaseConnection(); } - this.conn.releaseConnection(); - this.conn = null; } finally { cleanup(); } @@ -258,7 +257,7 @@ public class HttpResponseWrapper implements HttpResponse, ConnectionReleaseTrigg public boolean streamClosed(InputStream wrapped) throws IOException { try { - boolean open = conn != null && conn.isOpen(); + boolean open = connReleaseTrigger != null && !connReleaseTrigger.isReleased(); // this assumes that closing the stream will // consume the remainder of the response body: try { @@ -284,8 +283,8 @@ public class HttpResponseWrapper implements HttpResponse, ConnectionReleaseTrigg public static HttpResponseWrapper wrap( final HttpResponse response, - final ManagedClientConnection conn) { - return new HttpResponseWrapper(response, conn); + final ConnectionReleaseTriggerImpl connReleaseTrigger) { + return new HttpResponseWrapper(response, connReleaseTrigger); } } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/exec/MainClientExec.java b/httpclient/src/main/java/org/apache/http/impl/client/exec/MainClientExec.java index ab754a4ea..e173147a7 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/exec/MainClientExec.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/exec/MainClientExec.java @@ -34,6 +34,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.ConnectionReuseStrategy; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpHost; @@ -52,15 +53,13 @@ import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.params.HttpClientParams; import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.protocol.RequestClientConnControl; -import org.apache.http.concurrent.Cancellable; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ClientConnectionRequest; import org.apache.http.conn.ConnectionKeepAliveStrategy; -import org.apache.http.conn.ConnectionReleaseTrigger; -import org.apache.http.conn.ManagedClientConnection; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.BasicRouteDirector; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRouteDirector; +import org.apache.http.conn.routing.RouteTracker; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.entity.BufferedHttpEntity; @@ -113,7 +112,7 @@ public class MainClientExec implements ClientExecChain { private final Log log = LogFactory.getLog(getClass()); private final HttpRequestExecutor requestExecutor; - private final ClientConnectionManager connManager; + private final HttpClientConnectionManager connManager; private final ConnectionReuseStrategy reuseStrategy; private final ConnectionKeepAliveStrategy keepAliveStrategy; private final HttpProcessor proxyHttpProcessor; @@ -126,7 +125,7 @@ public class MainClientExec implements ClientExecChain { public MainClientExec( final HttpRequestExecutor requestExecutor, - final ClientConnectionManager connManager, + final HttpClientConnectionManager connManager, final ConnectionReuseStrategy reuseStrategy, final ConnectionKeepAliveStrategy keepAliveStrategy, final AuthenticationStrategy targetAuthStrategy, @@ -196,27 +195,20 @@ public class MainClientExec implements ClientExecChain { Object userToken = context.getAttribute(ClientContext.USER_TOKEN); - final ClientConnectionRequest connRequest = connManager.requestConnection(route, userToken); + final ConnectionRequest connRequest = connManager.requestConnection(route, userToken); if (execAware != null) { if (execAware.isAborted()) { - connRequest.abortRequest(); + connRequest.cancel(); throw new RequestAbortedException("Request aborted"); } else { - execAware.setCancellable(new Cancellable() { - - public boolean cancel() { - connRequest.abortRequest(); - return true; - } - - }); + execAware.setCancellable(connRequest); } } - ManagedClientConnection managedConn; + HttpClientConnection managedConn; try { long timeout = HttpClientParams.getConnectionManagerTimeout(params); - managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS); + managedConn = connRequest.get(timeout, TimeUnit.MILLISECONDS); } catch(InterruptedException interrupted) { throw new RequestAbortedException("Request aborted", interrupted); } @@ -234,28 +226,18 @@ public class MainClientExec implements ClientExecChain { } } - if (execAware != null) { - if (execAware.isAborted()) { - managedConn.releaseConnection(); - throw new RequestAbortedException("Request aborted"); - } else { - final ConnectionReleaseTrigger trigger = managedConn; - execAware.setCancellable(new Cancellable() { - - public boolean cancel() { - try { - trigger.abortConnection(); - } catch (IOException ex) { - log.debug("I/O error aborting connection", ex); - } - return true; - } - - }); - } - } - + ConnectionReleaseTriggerImpl releaseTrigger = new ConnectionReleaseTriggerImpl( + this.log, this.connManager, managedConn); try { + if (execAware != null) { + if (execAware.isAborted()) { + releaseTrigger.abortConnection(); + throw new RequestAbortedException("Request aborted"); + } else { + execAware.setCancellable(releaseTrigger); + } + } + HttpResponse response = null; for (int execCount = 1;; execCount++) { @@ -270,21 +252,19 @@ public class MainClientExec implements ClientExecChain { if (!managedConn.isOpen()) { this.log.debug("Opening connection " + route); - managedConn.open(route, context, params); + try { + establishRoute(proxyAuthState, managedConn, route, request, context); + } catch (TunnelRefusedException ex) { + if (this.log.isDebugEnabled()) { + this.log.debug(ex.getMessage()); + } + response = ex.getResponse(); + break; + } } else { managedConn.setSocketTimeout(HttpConnectionParams.getSoTimeout(params)); } - try { - establishRoute(proxyAuthState, managedConn, route, request, context); - } catch (TunnelRefusedException ex) { - if (this.log.isDebugEnabled()) { - this.log.debug(ex.getMessage()); - } - response = ex.getResponse(); - break; - } - if (execAware != null && execAware.isAborted()) { throw new RequestAbortedException("Request aborted"); } @@ -322,15 +302,15 @@ public class MainClientExec implements ClientExecChain { } this.log.debug("Connection can be kept alive " + s); } - managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS); - managedConn.markReusable(); + releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS); + releaseTrigger.markReusable(); } else { - managedConn.unmarkReusable(); + releaseTrigger.markNonReusable(); } if (needAuthentication( targetAuthState, proxyAuthState, route, request, response, context)) { - if (managedConn.isMarkedReusable()) { + if (releaseTrigger.isReusable()) { // Make sure the response body is fully consumed, if present HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); @@ -364,21 +344,17 @@ public class MainClientExec implements ClientExecChain { context.setAttribute(ClientContext.USER_TOKEN, userToken); } if (userToken != null) { - managedConn.setState(userToken); + releaseTrigger.setState(userToken); } // check for entity, release connection if possible HttpEntity entity = response.getEntity(); if (entity == null || !entity.isStreaming()) { // connection not needed and (assumed to be) in re-usable state - try { - managedConn.releaseConnection(); - } catch(IOException ex) { - this.log.debug("IOException releasing connection", ex); - } + releaseTrigger.releaseConnection(); return HttpResponseWrapper.wrap(response, null); } else { - return HttpResponseWrapper.wrap(response, managedConn); + return HttpResponseWrapper.wrap(response, releaseTrigger); } } catch (ConnectionShutdownException ex) { InterruptedIOException ioex = new InterruptedIOException( @@ -386,13 +362,13 @@ public class MainClientExec implements ClientExecChain { ioex.initCause(ex); throw ioex; } catch (HttpException ex) { - abortConnection(managedConn); + releaseTrigger.abortConnection(); throw ex; } catch (IOException ex) { - abortConnection(managedConn); + releaseTrigger.abortConnection(); throw ex; } catch (RuntimeException ex) { - abortConnection(managedConn); + releaseTrigger.abortConnection(); throw ex; } } @@ -402,27 +378,34 @@ public class MainClientExec implements ClientExecChain { */ private void establishRoute( final AuthState proxyAuthState, - final ManagedClientConnection managedConn, + final HttpClientConnection managedConn, final HttpRoute route, final HttpRequest request, final HttpContext context) throws HttpException, IOException { HttpParams params = request.getParams(); + RouteTracker tracker = new RouteTracker(route); int step; do { - HttpRoute fact = managedConn.getRoute(); + HttpRoute fact = tracker.toRoute(); step = this.routeDirector.nextStep(route, fact); switch (step) { case HttpRouteDirector.CONNECT_TARGET: - case HttpRouteDirector.CONNECT_PROXY: - managedConn.open(route, context, params); + this.connManager.connect( + managedConn, route.getTargetHost(), route.getLocalAddress(), context, params); + tracker.connectTarget(route.isSecure()); + break; + case HttpRouteDirector.CONNECT_PROXY: + this.connManager.connect( + managedConn, route.getProxyHost(), route.getLocalAddress(), context, params); + HttpHost proxy = route.getProxyHost(); + tracker.connectProxy(proxy, false); break; - case HttpRouteDirector.TUNNEL_TARGET: { boolean secure = createTunnelToTarget(proxyAuthState, managedConn, route, request, context); this.log.debug("Tunnel to target created."); - managedConn.tunnelTarget(secure, params); + tracker.tunnelTarget(secure); } break; case HttpRouteDirector.TUNNEL_PROXY: { @@ -433,11 +416,11 @@ public class MainClientExec implements ClientExecChain { final int hop = fact.getHopCount()-1; // the hop to establish boolean secure = createTunnelToProxy(route, hop, context); this.log.debug("Tunnel to proxy created."); - managedConn.tunnelProxy(route.getHopTarget(hop), secure, params); + tracker.tunnelProxy(route.getHopTarget(hop), secure); } break; case HttpRouteDirector.LAYER_PROTOCOL: - managedConn.layerProtocol(context, params); + this.connManager.upgrade(managedConn, route.getTargetHost(), context, params); break; case HttpRouteDirector.UNREACHABLE: @@ -473,7 +456,7 @@ public class MainClientExec implements ClientExecChain { */ private boolean createTunnelToTarget( final AuthState proxyAuthState, - final ManagedClientConnection managedConn, + final HttpClientConnection managedConn, final HttpRoute route, final HttpRequest request, final HttpContext context) throws HttpException, IOException { @@ -505,7 +488,8 @@ public class MainClientExec implements ClientExecChain { for (;;) { if (!managedConn.isOpen()) { - managedConn.open(route, context, params); + this.connManager.connect( + managedConn, route.getProxyHost(), route.getLocalAddress(), context, params); } connect.removeHeaders(AUTH.PROXY_AUTH_RESP); @@ -557,8 +541,6 @@ public class MainClientExec implements ClientExecChain { response.getStatusLine(), response); } - managedConn.markReusable(); - // How to decide on security of the tunnelled connection? // The socket factory knows only about the segment to the proxy. // Even if that is secure, the hop to the target may be insecure. @@ -621,21 +603,4 @@ public class MainClientExec implements ClientExecChain { return false; } - /** - * Shuts down the connection. - * This method is called from a catch block in - * {@link #execute execute} during exception handling. - */ - private void abortConnection(final ManagedClientConnection managedConn) { - // we got here as the result of an exception - // no response will be returned, release the connection - try { - managedConn.abortConnection(); - } catch (IOException ex) { - if (this.log.isDebugEnabled()) { - this.log.debug(ex.getMessage(), ex); - } - } - } - } diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java index 958574b66..9ae29a3cc 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java @@ -59,8 +59,11 @@ import org.apache.http.conn.scheme.SchemeRegistry; * {@link PoolingClientConnectionManager}. * * @since 4.2 + * + * @deprecated (4.3) use {@link BasicHttpClientConnectionManager}. */ @ThreadSafe +@Deprecated public class BasicClientConnectionManager implements ClientConnectionManager { private final Log log = LogFactory.getLog(getClass()); diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java new file mode 100644 index 000000000..7b03788c6 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java @@ -0,0 +1,335 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpHost; +import org.apache.http.annotation.GuardedBy; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.HttpConnectionFactory; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; +import org.apache.http.util.LangUtils; + +/** + * A connection manager for a single connection. This connection manager maintains only one active + * connection. Even though this class is fully thread-safe it ought to be used by one execution + * thread only, as only one thread a time can lease the connection at a time. + *

+ * This connection manager will make an effort to reuse the connection for subsequent requests + * with the same {@link HttpRoute route}. It will, however, close the existing connection and + * open it for the given route, if the route of the persistent connection does not match that + * of the connection request. If the connection has been already been allocated + * {@link IllegalStateException} is thrown. + *

+ * This connection manager implementation should be used inside an EJB container instead of + * {@link PoolingHttpClientConnectionManager}. + * + * @since 4.3 + */ +@ThreadSafe +public class BasicHttpClientConnectionManager implements HttpClientConnectionManager { + + private final Log log = LogFactory.getLog(getClass()); + + private final HttpClientConnectionOperator connectionOperator; + private final HttpConnectionFactory connFactory; + + @GuardedBy("this") + private DefaultClientConnection conn; + + @GuardedBy("this") + private HttpRoute route; + + @GuardedBy("this") + private Object state; + + @GuardedBy("this") + private long updated; + + @GuardedBy("this") + private long expiry; + + @GuardedBy("this") + private boolean leased; + + @GuardedBy("this") + private volatile boolean shutdown; + + public BasicHttpClientConnectionManager( + final SchemeRegistry schemeRegistry, + final DnsResolver dnsResolver, + final HttpConnectionFactory connFactory) { + if (schemeRegistry == null) { + throw new IllegalArgumentException("Scheme registry may not be null"); + } + this.connectionOperator = new HttpClientConnectionOperator(schemeRegistry, dnsResolver); + this.connFactory = connFactory != null ? connFactory : DefaultClientConnectionFactory.INSTANCE; + this.expiry = Long.MAX_VALUE; + } + + public BasicHttpClientConnectionManager(final SchemeRegistry schemeRegistry) { + this(schemeRegistry, null, null); + } + + public BasicHttpClientConnectionManager() { + this(SchemeRegistryFactory.createDefault(), null, null); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public SchemeRegistry getSchemeRegistry() { + return this.connectionOperator.getSchemeRegistry(); + } + + HttpRoute getRoute() { + return route; + } + + Object getState() { + return state; + } + + public final ConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + if (route == null) { + throw new IllegalArgumentException("Route may not be null"); + } + return new ConnectionRequest() { + + public boolean cancel() { + // Nothing to abort, since requests are immediate. + return false; + } + + public HttpClientConnection get(long timeout, TimeUnit tunit) { + return BasicHttpClientConnectionManager.this.getConnection( + route, state); + } + + }; + } + + private void closeConnection() { + if (this.conn != null) { + this.log.debug("Closing connection"); + try { + this.conn.close(); + } catch (IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception closing connection", iox); + } + } + this.conn = null; + } + } + + private void shutdownConnection() { + if (this.conn != null) { + this.log.debug("Shutting down connection"); + try { + this.conn.shutdown(); + } catch (IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down connection", iox); + } + } + this.conn = null; + } + } + + private void checkExpiry() { + if (this.conn != null && System.currentTimeMillis() >= this.expiry) { + if (this.log.isDebugEnabled()) { + this.log.debug("Connection expired @ " + new Date(this.expiry)); + } + closeConnection(); + } + } + + synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) { + if (this.shutdown) { + throw new IllegalStateException("Connection manager has been shut down"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Get connection for route " + route); + } + if (this.leased) { + throw new IllegalStateException("Connection is still allocated"); + } + if (!LangUtils.equals(this.route, route) || !LangUtils.equals(this.state, state)) { + closeConnection(); + } + this.route = route; + this.state = state; + checkExpiry(); + if (this.conn == null) { + this.conn = this.connFactory.create(); + } + this.leased = true; + return this.conn; + } + + public synchronized void releaseConnection( + final HttpClientConnection conn, + final Object state, + long keepalive, final TimeUnit tunit) { + if (conn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + if (conn != this.conn) { + throw new IllegalArgumentException("Connection not obtained from this manager"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Releasing connection " + conn); + } + if (this.shutdown) { + shutdownConnection(); + return; + } + try { + this.updated = System.currentTimeMillis(); + if (!this.conn.isOpen()) { + this.conn = null; + this.route = null; + this.conn = null; + this.expiry = Long.MAX_VALUE; + } else { + this.state = state; + if (this.log.isDebugEnabled()) { + String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + if (keepalive > 0) { + this.expiry = this.updated + tunit.toMillis(keepalive); + } else { + this.expiry = Long.MAX_VALUE; + } + } + } finally { + this.leased = false; + } + } + + public void connect( + final HttpClientConnection conn, + final HttpHost host, + final InetAddress local, + final HttpContext context, + final HttpParams params) throws IOException { + if (conn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + if (host == null) { + throw new IllegalArgumentException("HTTP host may not be null"); + } + if (conn != this.conn) { + throw new IllegalArgumentException("Connection not obtained from this manager"); + } + this.connectionOperator.connect(this.conn, host, local, context, params); + } + + public void upgrade( + final HttpClientConnection conn, + final HttpHost host, + final HttpContext context, + final HttpParams params) throws IOException { + if (conn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + if (host == null) { + throw new IllegalArgumentException("HTTP host may not be null"); + } + if (conn != this.conn) { + throw new IllegalArgumentException("Connection not obtained from this manager"); + } + this.connectionOperator.upgrade(this.conn, host, context, params); + } + + public synchronized void closeExpiredConnections() { + if (this.shutdown) { + return; + } + if (!this.leased) { + checkExpiry(); + } + } + + public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) { + if (tunit == null) { + throw new IllegalArgumentException("Time unit must not be null."); + } + if (this.shutdown) { + return; + } + if (!this.leased) { + long time = tunit.toMillis(idletime); + if (time < 0) { + time = 0; + } + long deadline = System.currentTimeMillis() - time; + if (this.updated <= deadline) { + closeConnection(); + } + } + } + + public synchronized void shutdown() { + if (this.shutdown) { + return; + } + this.shutdown = true; + shutdownConnection(); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java b/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java new file mode 100644 index 000000000..69edcf23a --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/CPool.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * 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 + * . + * + */ +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.pool.AbstractConnPool; +import org.apache.http.pool.ConnFactory; + +/** + * @since 4.3 + */ +@ThreadSafe +class CPool extends AbstractConnPool { + + private static AtomicLong COUNTER = new AtomicLong(); + + private final Log log = LogFactory.getLog(HttpClientConnectionManager.class); + private final long timeToLive; + private final TimeUnit tunit; + + public CPool( + final int defaultMaxPerRoute, final int maxTotal, + final long timeToLive, final TimeUnit tunit) { + super(new InternalConnFactory(), defaultMaxPerRoute, maxTotal); + this.timeToLive = timeToLive; + this.tunit = tunit; + } + + @Override + protected CPoolEntry createEntry(final HttpRoute route, final DefaultClientConnection conn) { + String id = Long.toString(COUNTER.getAndIncrement()); + return new CPoolEntry(this.log, id, route, conn, this.timeToLive, this.tunit); + } + + static class InternalConnFactory implements ConnFactory { + + public DefaultClientConnection create(final HttpRoute route) throws IOException { + return new DefaultClientConnection(); + } + + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/CPoolEntry.java b/httpclient/src/main/java/org/apache/http/impl/conn/CPoolEntry.java new file mode 100644 index 000000000..0b43232aa --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/CPoolEntry.java @@ -0,0 +1,82 @@ +/* + * ==================================================================== + * 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 + * . + * + */ +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.http.HttpClientConnection; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.pool.PoolEntry; + +/** + * @since 4.3 + */ +@ThreadSafe +class CPoolEntry extends PoolEntry { + + private final Log log; + + public CPoolEntry( + final Log log, + final String id, + final HttpRoute route, + final DefaultClientConnection conn, + final long timeToLive, final TimeUnit tunit) { + super(id, route, conn, timeToLive, tunit); + this.log = log; + } + + @Override + public boolean isExpired(long now) { + boolean expired = super.isExpired(now); + if (expired && this.log.isDebugEnabled()) { + this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry())); + } + return expired; + } + + @Override + public boolean isClosed() { + HttpClientConnection conn = getConnection(); + return !conn.isOpen(); + } + + @Override + public void close() { + HttpClientConnection conn = getConnection(); + try { + conn.close(); + } catch (IOException ex) { + this.log.debug("I/O error closing connection", ex); + } + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/CPoolProxy.java b/httpclient/src/main/java/org/apache/http/impl/conn/CPoolProxy.java new file mode 100644 index 000000000..cafaf80c1 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/CPoolProxy.java @@ -0,0 +1,165 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.apache.http.HttpClientConnection; +import org.apache.http.annotation.NotThreadSafe; +import org.apache.http.conn.HttpSSLConnection; +import org.apache.http.protocol.HttpContext; + +/** + * @since 4.3 + */ +@NotThreadSafe +class CPoolProxy implements InvocationHandler { + + private volatile CPoolEntry poolEntry; + + CPoolProxy(final CPoolEntry entry) { + super(); + this.poolEntry = entry; + } + + CPoolEntry getPoolEntry() { + return this.poolEntry; + } + + CPoolEntry detach() { + CPoolEntry local = this.poolEntry; + this.poolEntry = null; + return local; + } + + HttpClientConnection getConnection() { + CPoolEntry local = this.poolEntry; + if (local == null) { + return null; + } + return local.getConnection(); + } + + public void close() throws IOException { + CPoolEntry local = this.poolEntry; + if (local != null) { + HttpClientConnection conn = local.getConnection(); + conn.close(); + } + } + + public void shutdown() throws IOException { + CPoolEntry local = this.poolEntry; + if (local != null) { + HttpClientConnection conn = local.getConnection(); + conn.shutdown(); + } + } + + public boolean isOpen() { + HttpClientConnection conn = getConnection(); + if (conn != null) { + return conn.isOpen(); + } else { + return false; + } + } + + public boolean isStale() { + HttpClientConnection conn = getConnection(); + if (conn != null) { + return conn.isStale(); + } else { + return true; + } + } + + public Object invoke( + final Object proxy, final Method method, final Object[] args) throws Throwable { + String mname = method.getName(); + if (mname.equals("close")) { + close(); + return null; + } else if (mname.equals("shutdown")) { + shutdown(); + return null; + } else if (mname.equals("isOpen")) { + return isOpen(); + } else if (mname.equals("isStale")) { + return isStale(); + } else { + HttpClientConnection conn = getConnection(); + if (conn == null) { + throw new ConnectionShutdownException(); + } + try { + return method.invoke(conn, args); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause(); + if (cause != null) { + throw cause; + } else { + throw ex; + } + } + } + } + + public static HttpClientConnection newProxy( + final CPoolEntry poolEntry) { + return (HttpClientConnection) Proxy.newProxyInstance( + CPoolProxy.class.getClassLoader(), + new Class[] { HttpClientConnection.class, HttpSSLConnection.class, HttpContext.class }, + new CPoolProxy(poolEntry)); + } + + private static CPoolProxy getHandler( + final HttpClientConnection proxy) { + InvocationHandler handler = Proxy.getInvocationHandler(proxy); + if (!CPoolProxy.class.isInstance(handler)) { + throw new IllegalStateException("Unexpected proxy handler class: " + handler); + } + return CPoolProxy.class.cast(handler); + } + + public static CPoolEntry getPoolEntry(final HttpClientConnection proxy) { + CPoolEntry entry = getHandler(proxy).getPoolEntry(); + if (entry == null) { + throw new ConnectionShutdownException(); + } + return entry; + } + + public static CPoolEntry detach(final HttpClientConnection proxy) { + return getHandler(proxy).detach(); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java index 4b612d9de..1a0857809 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java @@ -33,6 +33,9 @@ import java.net.Socket; import java.util.HashMap; import java.util.Map; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + import org.apache.http.annotation.NotThreadSafe; import org.apache.commons.logging.Log; @@ -51,6 +54,7 @@ import org.apache.http.io.HttpMessageParser; import org.apache.http.io.SessionInputBuffer; import org.apache.http.io.SessionOutputBuffer; +import org.apache.http.conn.HttpSSLConnection; import org.apache.http.conn.OperatedClientConnection; /** @@ -70,7 +74,7 @@ import org.apache.http.conn.OperatedClientConnection; */ @NotThreadSafe // connSecure, targetHost public class DefaultClientConnection extends SocketHttpClientConnection - implements OperatedClientConnection, HttpContext { + implements OperatedClientConnection, HttpSSLConnection, HttpContext { private final Log log = LogFactory.getLog(getClass()); private final Log headerLog = LogFactory.getLog("org.apache.http.headers"); @@ -109,6 +113,14 @@ public class DefaultClientConnection extends SocketHttpClientConnection return this.socket; } + public SSLSession getSSLSession() { + if (this.socket instanceof SSLSocket) { + return ((SSLSocket) this.socket).getSession(); + } else { + return null; + } + } + public void opening(Socket sock, HttpHost target) throws IOException { assertNotOpen(); this.socket = sock; diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionFactory.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionFactory.java new file mode 100644 index 000000000..80ae5967f --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionFactory.java @@ -0,0 +1,45 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import org.apache.http.annotation.Immutable; +import org.apache.http.conn.HttpConnectionFactory; + +/** + * @since 4.3 + */ +@Immutable +public class DefaultClientConnectionFactory implements HttpConnectionFactory { + + public static final DefaultClientConnectionFactory INSTANCE = new DefaultClientConnectionFactory(); + + public DefaultClientConnection create() { + return new DefaultClientConnection(); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java index b94e00020..e3cf156c9 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java @@ -84,7 +84,10 @@ import org.apache.http.conn.DnsResolver; * * * @since 4.0 + * + * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}. */ +@Deprecated @ThreadSafe public class DefaultClientConnectionOperator implements ClientConnectionOperator { diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java new file mode 100644 index 000000000..d66068be9 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java @@ -0,0 +1,215 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpHost; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.conn.ConnectionPoolTimeoutException; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.params.HttpParams; +import org.apache.http.pool.ConnPool; +import org.apache.http.protocol.HttpContext; + +/** + * Base class for {@link HttpClientConnectionManager} implementations. + * This class primarily provides consistent implementation of + * {@link #connect(HttpClientConnection, HttpHost, InetAddress, HttpContext, HttpParams)} + * and {@link #upgrade(HttpClientConnection, HttpHost, HttpContext, HttpParams)} + * methods for all standard connection managers shipped with HttpClient. + * + * @since 4.3 + */ +@ThreadSafe +abstract class HttpClientConnectionManagerBase implements HttpClientConnectionManager { + + private final ConnPool pool; + private final HttpClientConnectionOperator connectionOperator; + + HttpClientConnectionManagerBase( + final ConnPool pool, + final SchemeRegistry schemeRegistry, + final DnsResolver dnsResolver) { + super(); + if (pool == null) { + throw new IllegalArgumentException("Connection pool may nor be null"); + } + if (schemeRegistry == null) { + throw new IllegalArgumentException("Scheme registry may nor be null"); + } + this.pool = pool; + this.connectionOperator = new HttpClientConnectionOperator(schemeRegistry, dnsResolver); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + public SchemeRegistry getSchemeRegistry() { + return this.connectionOperator.getSchemeRegistry(); + } + + protected void onConnectionLeaseRequest(final HttpRoute route, final Object state) { + } + + protected void onConnectionLease(final CPoolEntry entry) { + } + + protected void onConnectionKeepAlive(final CPoolEntry entry) { + } + + protected void onConnectionRelease(final CPoolEntry entry) { + } + + public ConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + if (route == null) { + throw new IllegalArgumentException("HTTP route may not be null"); + } + onConnectionLeaseRequest(route, state); + final Future future = this.pool.lease(route, state, null); + return new ConnectionRequest() { + + public boolean cancel() { + return future.cancel(true); + } + + public HttpClientConnection get( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + return leaseConnection(future, timeout, tunit); + } + + }; + + } + + protected HttpClientConnection leaseConnection( + final Future future, + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + CPoolEntry entry; + try { + entry = future.get(timeout, tunit); + if (entry == null || future.isCancelled()) { + throw new InterruptedException(); + } + if (entry.getConnection() == null) { + throw new IllegalStateException("Pool entry with no connection"); + } + onConnectionLease(entry); + return CPoolProxy.newProxy(entry); + } catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause == null) { + cause = ex; + } + InterruptedException intex = new InterruptedException(); + intex.initCause(cause); + throw intex; + } catch (TimeoutException ex) { + throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); + } + } + + public void releaseConnection( + final HttpClientConnection managedConn, + final Object state, + final long keepalive, final TimeUnit tunit) { + if (managedConn == null) { + throw new IllegalArgumentException("Managed connection may not be null"); + } + synchronized (managedConn) { + CPoolEntry entry = CPoolProxy.detach(managedConn); + if (entry == null) { + return; + } + DefaultClientConnection conn = entry.getConnection(); + try { + if (conn.isOpen()) { + entry.setState(state); + entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + onConnectionKeepAlive(entry); + } + } finally { + this.pool.release(entry, conn.isOpen()); + onConnectionRelease(entry); + } + } + } + + public void connect( + final HttpClientConnection managedConn, + final HttpHost host, + final InetAddress local, + final HttpContext context, + final HttpParams params) throws IOException { + if (managedConn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + DefaultClientConnection conn; + synchronized (managedConn) { + CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + this.connectionOperator.connect(conn, host, local, context, params); + } + + public void upgrade( + final HttpClientConnection managedConn, + final HttpHost host, + final HttpContext context, + final HttpParams params) throws IOException { + if (managedConn == null) { + throw new IllegalArgumentException("Connection may not be null"); + } + DefaultClientConnection conn; + synchronized (managedConn) { + CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + this.connectionOperator.upgrade(conn, host, context, params); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionOperator.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionOperator.java new file mode 100644 index 000000000..314f360fb --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionOperator.java @@ -0,0 +1,163 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpHost; +import org.apache.http.annotation.Immutable; +import org.apache.http.client.protocol.ClientContext; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.HttpHostConnectException; +import org.apache.http.conn.HttpInetSocketAddress; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeLayeredSocketFactory; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.scheme.SchemeSocketFactory; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; + +@Immutable +class HttpClientConnectionOperator { + + private final Log log = LogFactory.getLog(HttpClientConnectionManager.class); + + private final SchemeRegistry schemeRegistry; + private final DnsResolver dnsResolver; + + HttpClientConnectionOperator( + final SchemeRegistry schemeRegistry, + final DnsResolver dnsResolver) { + super(); + if (schemeRegistry == null) { + throw new IllegalArgumentException("Scheme registry may nor be null"); + } + this.schemeRegistry = schemeRegistry; + this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE; + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + public DnsResolver getDnsResolver() { + return this.dnsResolver; + } + + private SchemeRegistry getSchemeRegistry(final HttpContext context) { + SchemeRegistry reg = (SchemeRegistry) context.getAttribute( + ClientContext.SCHEME_REGISTRY); + if (reg == null) { + reg = this.schemeRegistry; + } + return reg; + } + + public void connect( + final DefaultClientConnection conn, + final HttpHost host, + final InetAddress local, + final HttpContext context, + final HttpParams params) throws IOException { + SchemeRegistry registry = getSchemeRegistry(context); + Scheme schm = registry.getScheme(host.getSchemeName()); + SchemeSocketFactory sf = schm.getSchemeSocketFactory(); + + InetAddress[] addresses = this.dnsResolver.resolve(host.getHostName()); + int port = schm.resolvePort(host.getPort()); + for (int i = 0; i < addresses.length; i++) { + InetAddress address = addresses[i]; + boolean last = i == addresses.length - 1; + + Socket sock = sf.createSocket(params); + conn.opening(sock, host); + + InetSocketAddress remoteAddress = new HttpInetSocketAddress(host, address, port); + InetSocketAddress localAddress = null; + if (local != null) { + localAddress = new InetSocketAddress(local, 0); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connecting to " + remoteAddress); + } + try { + Socket connsock = sf.connectSocket(sock, remoteAddress, localAddress, params); + if (sock != connsock) { + sock = connsock; + conn.opening(sock, host); + } + conn.openCompleted(sf.isSecure(sock), params); + return; + } catch (ConnectException ex) { + if (last) { + throw new HttpHostConnectException(host, ex); + } + } catch (ConnectTimeoutException ex) { + if (last) { + throw ex; + } + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connect to " + remoteAddress + " timed out. " + + "Connection will be retried using another IP address"); + } + } + } + + public void upgrade( + final DefaultClientConnection conn, + final HttpHost host, + final HttpContext context, + final HttpParams params) throws IOException { + SchemeRegistry registry = getSchemeRegistry(context); + Scheme schm = registry.getScheme(host.getSchemeName()); + if (!(schm.getSchemeSocketFactory() instanceof SchemeLayeredSocketFactory)) { + throw new IllegalArgumentException + ("Target scheme (" + schm.getName() + + ") must have layered socket factory."); + } + SchemeLayeredSocketFactory lsf = (SchemeLayeredSocketFactory) schm.getSchemeSocketFactory(); + Socket sock; + try { + int port = schm.resolvePort(host.getPort()); + sock = lsf.createLayeredSocket( + conn.getSocket(), host.getHostName(), port, params); + } catch (ConnectException ex) { + throw new HttpHostConnectException(host, ex); + } + conn.update(sock, host, lsf.isSecure(sock), params); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java index ee27b759b..714654d84 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java @@ -38,7 +38,10 @@ import org.apache.http.pool.ConnFactory; /** * @since 4.2 + * + * @deprecated (4.3) no longer used. */ +@Deprecated class HttpConnPool extends AbstractConnPool { private static AtomicLong COUNTER = new AtomicLong(); diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java index 71ac85bdb..5857f407b 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java @@ -38,7 +38,10 @@ import org.apache.http.pool.PoolEntry; /** * @since 4.2 + * + * @deprecated (4.3) no longer used. */ +@Deprecated class HttpPoolEntry extends PoolEntry { private final Log log; diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java b/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java index 83df90e90..5b46da1d8 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java @@ -51,6 +51,12 @@ import org.apache.http.conn.routing.RouteTracker; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; +/** + * @since 4.2 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated @NotThreadSafe class ManagedClientConnectionImpl implements ManagedClientConnection { diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java index 2bcba16cd..1a3f6329e 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java @@ -66,7 +66,10 @@ import org.apache.http.conn.DnsResolver; * can be adjusted using HTTP parameters. * * @since 4.2 + * + * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}. */ +@Deprecated @ThreadSafe public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl { diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java new file mode 100644 index 000000000..f8a5562f5 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java @@ -0,0 +1,231 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpClientConnection; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.pool.ConnPoolControl; +import org.apache.http.pool.PoolStats; + +/** + * ClientConnectionPoolManager maintains a pool of + * {@link HttpClientConnection}s and is able to service connection requests + * from multiple execution threads. Connections are pooled on a per route + * basis. A request for a route which already the manager has persistent + * connections for available in the pool will be services by leasing + * a connection from the pool rather than creating a brand new connection. + *

+ * ClientConnectionPoolManager maintains a maximum limit of connection + * on a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using {@link ConnPoolControl} methods. + * + * @since 4.3 + */ +@ThreadSafe +public class PoolingHttpClientConnectionManager extends HttpClientConnectionManagerBase { + + private final Log log = LogFactory.getLog(getClass()); + + private final CPool pool; + + public PoolingHttpClientConnectionManager() { + this(SchemeRegistryFactory.createDefault()); + } + + public PoolingHttpClientConnectionManager(final SchemeRegistry schreg) { + this(schreg, -1, TimeUnit.MILLISECONDS); + } + + public PoolingHttpClientConnectionManager( + final SchemeRegistry schreg, final DnsResolver dnsResolver) { + this(schreg, dnsResolver, -1, TimeUnit.MILLISECONDS); + } + + public PoolingHttpClientConnectionManager( + final SchemeRegistry schemeRegistry, + final long timeToLive, final TimeUnit tunit) { + this(new CPool(2, 20, timeToLive, tunit), schemeRegistry, null); + } + + public PoolingHttpClientConnectionManager( + final SchemeRegistry schemeRegistry, + final DnsResolver dnsResolver, + final long timeToLive, final TimeUnit tunit) { + this(new CPool(2, 20, timeToLive, tunit), schemeRegistry, dnsResolver); + } + + PoolingHttpClientConnectionManager( + final CPool pool, + final SchemeRegistry schemeRegistry, + final DnsResolver dnsResolver) { + super(pool, schemeRegistry, dnsResolver); + this.pool = pool; + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + private String format(final HttpRoute route, final Object state) { + StringBuilder buf = new StringBuilder(); + buf.append("[route: ").append(route).append("]"); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + private String formatStats(final HttpRoute route) { + StringBuilder buf = new StringBuilder(); + PoolStats totals = this.pool.getTotalStats(); + PoolStats stats = this.pool.getStats(route); + buf.append("[total kept alive: ").append(totals.getAvailable()).append("; "); + buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable()); + buf.append(" of ").append(stats.getMax()).append("; "); + buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); + buf.append(" of ").append(totals.getMax()).append("]"); + return buf.toString(); + } + + private String format(final CPoolEntry entry) { + StringBuilder buf = new StringBuilder(); + buf.append("[id: ").append(entry.getId()).append("]"); + buf.append("[route: ").append(entry.getRoute()).append("]"); + Object state = entry.getState(); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + @Override + protected void onConnectionLeaseRequest(final HttpRoute route, final Object state) { + if (this.log.isDebugEnabled()) { + this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + } + } + + @Override + protected void onConnectionLease(final CPoolEntry entry) { + if (this.log.isDebugEnabled()) { + this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + } + } + + @Override + protected void onConnectionKeepAlive(final CPoolEntry entry) { + if (this.log.isDebugEnabled()) { + long keepalive = entry.getExpiry(); + String s; + if (keepalive > 0) { + s = "for " + (double) keepalive / 1000 + " seconds"; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + } + } + + @Override + protected void onConnectionRelease(final CPoolEntry entry) { + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + } + } + + public void shutdown() { + this.log.debug("Connection manager is shutting down"); + try { + this.pool.shutdown(); + } catch (IOException ex) { + this.log.debug("I/O exception shutting down connection manager", ex); + } + this.log.debug("Connection manager shut down"); + } + + public void closeIdleConnections(long idleTimeout, TimeUnit tunit) { + if (this.log.isDebugEnabled()) { + this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + this.pool.closeIdle(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + this.log.debug("Closing expired connections"); + this.pool.closeExpired(); + } + + public int getMaxTotal() { + return this.pool.getMaxTotal(); + } + + public void setMaxTotal(int max) { + this.pool.setMaxTotal(max); + } + + public int getDefaultMaxPerRoute() { + return this.pool.getDefaultMaxPerRoute(); + } + + public void setDefaultMaxPerRoute(int max) { + this.pool.setDefaultMaxPerRoute(max); + } + + public int getMaxPerRoute(final HttpRoute route) { + return this.pool.getMaxPerRoute(route); + } + + public void setMaxPerRoute(final HttpRoute route, int max) { + this.pool.setMaxPerRoute(route, max); + } + + public PoolStats getTotalStats() { + return this.pool.getTotalStats(); + } + + public PoolStats getStats(final HttpRoute route) { + return this.pool.getStats(route); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java b/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java index 1196d9527..298b55166 100644 --- a/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java +++ b/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java @@ -38,6 +38,8 @@ import org.apache.http.conn.DnsResolver; */ public class SystemDefaultDnsResolver implements DnsResolver { + public static final SystemDefaultDnsResolver INSTANCE = new SystemDefaultDnsResolver(); + /** * {@inheritDoc} */ diff --git a/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java b/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java index 9ef4ec233..80f037898 100644 --- a/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java +++ b/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java @@ -39,7 +39,7 @@ import org.apache.http.HttpResponseInterceptor; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.localserver.LocalTestServer; import org.apache.http.localserver.RandomHandler; import org.apache.http.protocol.BasicHttpProcessor; @@ -79,7 +79,7 @@ public class TestConnectionReuse { InetSocketAddress saddress = this.localServer.getServiceAddress(); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(5); mgr.setDefaultMaxPerRoute(5); @@ -138,7 +138,7 @@ public class TestConnectionReuse { InetSocketAddress saddress = this.localServer.getServiceAddress(); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(5); mgr.setDefaultMaxPerRoute(5); @@ -188,7 +188,7 @@ public class TestConnectionReuse { InetSocketAddress saddress = this.localServer.getServiceAddress(); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(5); mgr.setDefaultMaxPerRoute(5); @@ -239,7 +239,7 @@ public class TestConnectionReuse { InetSocketAddress saddress = this.localServer.getServiceAddress(); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(1); mgr.setDefaultMaxPerRoute(1); diff --git a/httpclient/src/test/java/org/apache/http/conn/params/TestRouteParams.java b/httpclient/src/test/java/org/apache/http/conn/params/TestRouteParams.java deleted file mode 100644 index 0cffc6e42..000000000 --- a/httpclient/src/test/java/org/apache/http/conn/params/TestRouteParams.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.conn.params; - -import java.net.InetAddress; - -import org.apache.http.HttpHost; -import org.apache.http.params.HttpParams; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.DefaultedHttpParams; -import org.apache.http.conn.routing.HttpRoute; - -// for hierarchy testing -import org.apache.http.impl.client.ClientParamsStack; -import org.junit.Assert; -import org.junit.Test; - -/** - * Unit tests for parameters. - * Trivial, but it looks better in the Clover reports. - */ -public class TestRouteParams { - - public final static - HttpHost TARGET1 = new HttpHost("target1.test.invalid"); - public final static - HttpRoute ROUTE1 = new HttpRoute(TARGET1); - public final static InetAddress LOCAL1; - - // need static initializer to deal with exceptions - static { - try { - LOCAL1 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 }); - } catch (Exception x) { - throw new ExceptionInInitializerError(x); - } - } - - @Test - public void testSetGet() { - HttpParams params = new BasicHttpParams(); - - Assert.assertNull("phantom proxy", - ConnRouteParams.getDefaultProxy(params)); - Assert.assertNull("phantom route", - ConnRouteParams.getForcedRoute(params)); - Assert.assertNull("phantom address", - ConnRouteParams.getLocalAddress(params)); - - ConnRouteParams.setDefaultProxy(params, TARGET1); - Assert.assertSame("wrong proxy", TARGET1, - ConnRouteParams.getDefaultProxy(params)); - ConnRouteParams.setForcedRoute(params, ROUTE1); - Assert.assertSame("wrong route", ROUTE1, - ConnRouteParams.getForcedRoute(params)); - ConnRouteParams.setLocalAddress(params, LOCAL1); - Assert.assertSame("wrong address", LOCAL1, - ConnRouteParams.getLocalAddress(params)); - } - - @Test - public void testSetNull() { - HttpParams params = new BasicHttpParams(); - - ConnRouteParams.setDefaultProxy(params, null); - ConnRouteParams.setForcedRoute(params, null); - ConnRouteParams.setLocalAddress(params, null); - - Assert.assertNull("phantom proxy", - ConnRouteParams.getDefaultProxy(params)); - Assert.assertNull("phantom route", - ConnRouteParams.getForcedRoute(params)); - Assert.assertNull("phantom address", - ConnRouteParams.getLocalAddress(params)); - - ConnRouteParams.setDefaultProxy(params, ConnRouteParams.NO_HOST); - Assert.assertNull("null proxy not detected", - ConnRouteParams.getDefaultProxy(params)); - - ConnRouteParams.setForcedRoute(params, ConnRouteParams.NO_ROUTE); - Assert.assertNull("null route not detected", - ConnRouteParams.getForcedRoute(params)); - } - - @Test - public void testUnsetHierarchy() { - // hierarchical unsetting is only tested for the default proxy - HttpParams daddy = new BasicHttpParams(); - HttpParams dummy = new BasicHttpParams(); - HttpParams child = new BasicHttpParams(); - - ConnRouteParams.setDefaultProxy(daddy, TARGET1); - ConnRouteParams.setDefaultProxy(child, ConnRouteParams.NO_HOST); - - HttpParams hierarchy = - new ClientParamsStack(null, daddy, child, null); - Assert.assertNull("1", ConnRouteParams.getDefaultProxy(hierarchy)); - - hierarchy = new ClientParamsStack - (null, - daddy, - new ClientParamsStack(null, child, dummy, null), - null); - Assert.assertNull("2", ConnRouteParams.getDefaultProxy(hierarchy)); - - hierarchy = new ClientParamsStack - (null, daddy, new DefaultedHttpParams(child, dummy), null); - Assert.assertNull("3", ConnRouteParams.getDefaultProxy(hierarchy)); - - hierarchy = new DefaultedHttpParams(child, daddy); - Assert.assertNull("4", ConnRouteParams.getDefaultProxy(hierarchy)); - - hierarchy = new DefaultedHttpParams - (new DefaultedHttpParams(child, dummy), daddy); - Assert.assertNull("5", ConnRouteParams.getDefaultProxy(hierarchy)); - - hierarchy = new DefaultedHttpParams - (child, new DefaultedHttpParams(dummy, daddy)); - Assert.assertNull("6", ConnRouteParams.getDefaultProxy(hierarchy)); - } - - @Test - public void testBadArgs() { - - try { - ConnRouteParams.getDefaultProxy(null); - } catch (IllegalArgumentException iax) { - // expected - } - - try { - ConnRouteParams.getForcedRoute(null); - } catch (IllegalArgumentException iax) { - // expected - } - - try { - ConnRouteParams.getLocalAddress(null); - } catch (IllegalArgumentException iax) { - // expected - } - - try { - ConnRouteParams.setDefaultProxy(null, null); - } catch (IllegalArgumentException iax) { - // expected - } - - try { - ConnRouteParams.setForcedRoute(null, null); - } catch (IllegalArgumentException iax) { - // expected - } - - try { - ConnRouteParams.setLocalAddress(null, null); - } catch (IllegalArgumentException iax) { - // expected - } - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java b/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java index 5eada02d6..b62a73d1a 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java @@ -48,6 +48,7 @@ import org.apache.http.protocol.HttpContext; import org.junit.Before; import org.junit.Test; +@Deprecated public class TestAutoRetryHttpClient{ private AutoRetryHttpClient impl; diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java b/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java index f54254ecf..af4943a4a 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java @@ -34,12 +34,12 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.scheme.SchemeSocketFactory; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; @@ -54,7 +54,7 @@ public class TestRequestRetryHandler { SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", 80, new OppsieSchemeSocketFactory())); - ClientConnectionManager connManager = new PoolingClientConnectionManager(schemeRegistry); + HttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(schemeRegistry); TestHttpRequestRetryHandler testRetryHandler = new TestHttpRequestRetryHandler(); HttpClient client = new HttpClientBuilder() diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestAbortHandling.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestAbortHandling.java index 5eab12bce..5966bd9d6 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestAbortHandling.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestAbortHandling.java @@ -28,10 +28,12 @@ package org.apache.http.impl.client.integration; import java.io.IOException; import java.net.ConnectException; +import java.net.InetAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; @@ -41,16 +43,15 @@ import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.Cancellable; -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.ConnectionRequest; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.SchemeRegistryFactory; import org.apache.http.message.BasicHeader; import org.apache.http.mockup.SocketFactoryMockup; @@ -171,7 +172,7 @@ public class TestAbortHandling extends IntegrationTestBase { this.localServer.register("*", new BasicService()); final CountDownLatch releaseLatch = new CountDownLatch(1); - final PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(); + final PoolingHttpClientConnectionManager conMan = new PoolingHttpClientConnectionManager(); final AtomicReference throwableRef = new AtomicReference(); final CountDownLatch getLatch = new CountDownLatch(1); final HttpClient client = new HttpClientBuilder().setConnectionManager(conMan).build(); @@ -210,7 +211,7 @@ public class TestAbortHandling extends IntegrationTestBase { public void testAbortBeforeExecute() throws Exception { this.localServer.register("*", new BasicService()); - final PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(); + final PoolingHttpClientConnectionManager conMan = new PoolingHttpClientConnectionManager(); final AtomicReference throwableRef = new AtomicReference(); final CountDownLatch getLatch = new CountDownLatch(1); final CountDownLatch startLatch = new CountDownLatch(1); @@ -296,15 +297,17 @@ public class TestAbortHandling extends IntegrationTestBase { */ @Test public void testSocketConnectFailureReleasesConnection() throws Exception { - ManagedClientConnection conn = Mockito.mock(ManagedClientConnection.class); - Mockito.doThrow(new ConnectException()).when(conn).open( - Mockito.any(HttpRoute.class), + HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); + ConnectionRequest connrequest = Mockito.mock(ConnectionRequest.class); + Mockito.when(connrequest.get( + Mockito.anyInt(), Mockito.any(TimeUnit.class))).thenReturn(conn); + HttpClientConnectionManager connmgr = Mockito.mock(HttpClientConnectionManager.class); + Mockito.doThrow(new ConnectException()).when(connmgr).connect( + Mockito.any(HttpClientConnection.class), + Mockito.any(HttpHost.class), + Mockito.any(InetAddress.class), Mockito.any(HttpContext.class), Mockito.any(HttpParams.class)); - ClientConnectionRequest connrequest = Mockito.mock(ClientConnectionRequest.class); - Mockito.when(connrequest.getConnection( - Mockito.anyInt(), Mockito.any(TimeUnit.class))).thenReturn(conn); - ClientConnectionManager connmgr = Mockito.mock(ClientConnectionManager.class); SchemeRegistry schemeRegistry = SchemeRegistryFactory.createDefault(); @@ -321,7 +324,7 @@ public class TestAbortHandling extends IntegrationTestBase { Assert.fail("expected IOException"); } catch(IOException expected) {} - Mockito.verify(conn).abortConnection(); + Mockito.verify(connmgr).releaseConnection(conn, null, 0, TimeUnit.MILLISECONDS); } private static class BasicService implements HttpRequestHandler { @@ -352,7 +355,7 @@ public class TestAbortHandling extends IntegrationTestBase { } } - private static class ConnMan4 extends PoolingClientConnectionManager { + private static class ConnMan4 extends PoolingHttpClientConnectionManager { private final CountDownLatch connLatch; private final CountDownLatch awaitLatch; @@ -363,19 +366,20 @@ public class TestAbortHandling extends IntegrationTestBase { } @Override - public ClientConnectionRequest requestConnection(HttpRoute route, Object state) { + public ConnectionRequest requestConnection(HttpRoute route, Object state) { // If this is the redirect route, stub the return value // so-as to pretend the host is waiting on a slot... if(route.getTargetHost().getHostName().equals("localhost")) { final Thread currentThread = Thread.currentThread(); - return new ClientConnectionRequest() { + return new ConnectionRequest() { - public void abortRequest() { + public boolean cancel() { currentThread.interrupt(); + return true; } - public ManagedClientConnection getConnection( + public HttpClientConnection get( long timeout, TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { @@ -388,7 +392,7 @@ public class TestAbortHandling extends IntegrationTestBase { if(!awaitLatch.await(timeout, tunit)) throw new ConnectionPoolTimeoutException(); - return Mockito.mock(ManagedClientConnection.class); + return Mockito.mock(HttpClientConnection.class); } }; } else { @@ -398,7 +402,7 @@ public class TestAbortHandling extends IntegrationTestBase { } - static class ConMan implements ClientConnectionManager { + static class ConMan implements HttpClientConnectionManager { private final CountDownLatch connLatch; private final CountDownLatch awaitLatch; @@ -415,28 +419,25 @@ public class TestAbortHandling extends IntegrationTestBase { throw new UnsupportedOperationException("just a mockup"); } - public ManagedClientConnection getConnection(HttpRoute route) { - throw new UnsupportedOperationException("just a mockup"); - } - - public ManagedClientConnection getConnection(HttpRoute route, + public HttpClientConnection getConnection(HttpRoute route, long timeout, TimeUnit tunit) { throw new UnsupportedOperationException("just a mockup"); } - public ClientConnectionRequest requestConnection( + public ConnectionRequest requestConnection( final HttpRoute route, final Object state) { final Thread currentThread = Thread.currentThread(); - return new ClientConnectionRequest() { + return new ConnectionRequest() { - public void abortRequest() { + public boolean cancel() { currentThread.interrupt(); + return true; } - public ManagedClientConnection getConnection( + public HttpClientConnection get( long timeout, TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { @@ -449,8 +450,9 @@ public class TestAbortHandling extends IntegrationTestBase { if(!awaitLatch.await(timeout, tunit)) throw new ConnectionPoolTimeoutException(); - return Mockito.mock(ManagedClientConnection.class); + return Mockito.mock(HttpClientConnection.class); } + }; } @@ -464,11 +466,22 @@ public class TestAbortHandling extends IntegrationTestBase { return registry; } - public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) { + public void shutdown() { + } + + public void releaseConnection(HttpClientConnection conn, Object newState, + long validDuration, TimeUnit timeUnit) { throw new UnsupportedOperationException("just a mockup"); } - public void shutdown() { + public void connect(HttpClientConnection conn, HttpHost host, InetAddress localAddress, + HttpContext context, HttpParams params) throws IOException { + throw new UnsupportedOperationException("just a mockup"); + } + + public void upgrade(HttpClientConnection conn, HttpHost host, HttpContext context, + HttpParams params) throws IOException { + throw new UnsupportedOperationException("just a mockup"); } } diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionAutoRelease.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionAutoRelease.java index bcbc3f02a..c439ac624 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionAutoRelease.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionAutoRelease.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.TimeUnit; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpHost; @@ -39,14 +40,13 @@ import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.MalformedChunkCodingException; import org.apache.http.client.methods.HttpGet; -import org.apache.http.conn.ClientConnectionRequest; +import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.ManagedClientConnection; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.entity.BasicHttpEntity; import org.apache.http.impl.DefaultHttpServerConnection; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.pool.PoolStats; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; @@ -58,12 +58,12 @@ import org.junit.Test; public class TestConnectionAutoRelease extends IntegrationTestBase { - private PoolingClientConnectionManager mgr; + private PoolingHttpClientConnectionManager mgr; @Before public void setUp() throws Exception { startServer(); - this.mgr = new PoolingClientConnectionManager(); + this.mgr = new PoolingHttpClientConnectionManager(); this.httpclient = new HttpClientBuilder().setConnectionManager(this.mgr).build(); } @@ -82,9 +82,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { HttpResponse response = this.httpclient.execute(target, httpget); - ClientConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); + ConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); try { - connreq.getConnection(250, TimeUnit.MILLISECONDS); + connreq.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException expected) { } @@ -99,9 +99,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { // Make sure one connection is available connreq = this.mgr.requestConnection(new HttpRoute(target), null); - ManagedClientConnection conn = connreq.getConnection(250, TimeUnit.MILLISECONDS); + HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); - this.mgr.releaseConnection(conn, -1, null); + this.mgr.releaseConnection(conn, null, -1, null); } @Test @@ -119,9 +119,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { HttpResponse response = this.httpclient.execute(target, httpget); - ClientConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); + ConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); try { - connreq.getConnection(250, TimeUnit.MILLISECONDS); + connreq.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException expected) { } @@ -137,9 +137,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { // Make sure one connection is available connreq = this.mgr.requestConnection(new HttpRoute(target), null); - ManagedClientConnection conn = connreq.getConnection(250, TimeUnit.MILLISECONDS); + HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); - this.mgr.releaseConnection(conn, -1, null); + this.mgr.releaseConnection(conn, null, -1, null); } @Test @@ -157,9 +157,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { HttpResponse response = this.httpclient.execute(target, httpget); - ClientConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); + ConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); try { - connreq.getConnection(250, TimeUnit.MILLISECONDS); + connreq.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException expected) { } @@ -173,9 +173,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { // Make sure one connection is available connreq = this.mgr.requestConnection(new HttpRoute(target), null); - ManagedClientConnection conn = connreq.getConnection(250, TimeUnit.MILLISECONDS); + HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); - this.mgr.releaseConnection(conn, -1, null); + this.mgr.releaseConnection(conn, null, -1, null); } @Test @@ -224,9 +224,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { HttpResponse response = this.httpclient.execute(target, httpget); - ClientConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); + ConnectionRequest connreq = this.mgr.requestConnection(new HttpRoute(target), null); try { - connreq.getConnection(250, TimeUnit.MILLISECONDS); + connreq.get(250, TimeUnit.MILLISECONDS); Assert.fail("ConnectionPoolTimeoutException should have been thrown"); } catch (ConnectionPoolTimeoutException expected) { } @@ -246,9 +246,9 @@ public class TestConnectionAutoRelease extends IntegrationTestBase { // Make sure one connection is available connreq = this.mgr.requestConnection(new HttpRoute(target), null); - ManagedClientConnection conn = connreq.getConnection(250, TimeUnit.MILLISECONDS); + HttpClientConnection conn = connreq.get(250, TimeUnit.MILLISECONDS); - this.mgr.releaseConnection(conn, -1, null); + this.mgr.releaseConnection(conn, null, -1, null); } } diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManager.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java similarity index 69% rename from httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManager.java rename to httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java index 7569740a5..a3e47b3c4 100644 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManager.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestConnectionManagement.java @@ -25,10 +25,9 @@ * */ -package org.apache.http.impl.conn; +package org.apache.http.impl.client.integration; import java.io.IOException; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; @@ -37,24 +36,24 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ClientConnectionOperator; -import org.apache.http.conn.ClientConnectionRequest; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.ManagedClientConnection; -import org.apache.http.conn.OperatedClientConnection; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.scheme.SchemeSocketFactory; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.impl.conn.SchemeRegistryFactory; import org.apache.http.localserver.LocalServerTestBase; import org.apache.http.message.BasicHttpRequest; import org.apache.http.params.BasicHttpParams; @@ -73,76 +72,30 @@ import org.junit.Before; import org.junit.Test; /** - * Tests for PoolingClientConnectionManager that do require a server + * Tests for PoolingHttpClientConnectionManager that do require a server * to communicate with. */ -public class TestPoolingConnManager extends LocalServerTestBase { +public class TestConnectionManagement extends LocalServerTestBase { - @Before + @Before public void setup() throws Exception { startServer(); } - - /** - * Tests executing several requests in parallel. - */ - @Test - public void testParallelRequests() throws Exception { - // 3.x: TestHttpConnectionManager.testGetFromMultipleThreads - final int COUNT = 8; // adjust to execute more requests - - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); - mgr.setMaxTotal(COUNT/2); - mgr.setDefaultMaxPerRoute(COUNT/2); - - final HttpHost target = getServerHttp(); - final HttpRoute route = new HttpRoute(target, null, false); - final int rsplen = 8; - final String uri = "/random/" + rsplen; - - ExecReqThread[] threads = new ExecReqThread [COUNT]; - for (int i=0; i throwRef = new AtomicReference(); Thread abortingThread = new Thread(new Runnable() { public void run() { try { stallingSocketFactory.waitForState(); - conn.abortConnection(); + conn.shutdown(); + mgr.releaseConnection(conn, null, -1, null); connectLatch.countDown(); } catch (Throwable e) { throwRef.set(e); @@ -520,7 +467,7 @@ public class TestPoolingConnManager extends LocalServerTestBase { abortingThread.start(); try { - conn.open(route, context, params); + mgr.connect(conn, route.getTargetHost(), route.getLocalAddress(), context, params); Assert.fail("expected SocketException"); } catch(SocketException expected) {} @@ -532,10 +479,10 @@ public class TestPoolingConnManager extends LocalServerTestBase { Assert.assertEquals(0, localServer.getAcceptedConnectionCount()); // the connection is expected to be released back to the manager - ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); + HttpClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); Assert.assertFalse("connection should have been closed", conn2.isOpen()); - mgr.releaseConnection(conn2, -1, null); + mgr.releaseConnection(conn2, null, -1, null); mgr.shutdown(); } @@ -548,7 +495,7 @@ public class TestPoolingConnManager extends LocalServerTestBase { SchemeRegistry registry = new SchemeRegistry(); registry.register(scheme); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(registry); + final PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(registry); mgr.setMaxTotal(1); HttpHost target = getServerHttp(); @@ -556,14 +503,15 @@ public class TestPoolingConnManager extends LocalServerTestBase { HttpContext context = new BasicHttpContext(); HttpParams params = new BasicHttpParams(); - final ManagedClientConnection conn = getConnection(mgr, route); + final HttpClientConnection conn = getConnection(mgr, route); final AtomicReference throwRef = new AtomicReference(); Thread abortingThread = new Thread(new Runnable() { public void run() { try { stallingSocketFactory.waitForState(); - conn.abortConnection(); + conn.shutdown(); + mgr.releaseConnection(conn, null, -1, null); connectLatch.countDown(); } catch (Throwable e) { throwRef.set(e); @@ -573,7 +521,7 @@ public class TestPoolingConnManager extends LocalServerTestBase { abortingThread.start(); try { - conn.open(route, context, params); + mgr.connect(conn, route.getTargetHost(), route.getLocalAddress(), context, params); Assert.fail("expected exception"); } catch(IOException expected) { Assert.assertEquals("Connection already shutdown", expected.getMessage()); @@ -587,10 +535,10 @@ public class TestPoolingConnManager extends LocalServerTestBase { Assert.assertEquals(0, localServer.getAcceptedConnectionCount()); // the connection is expected to be released back to the manager - ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); + HttpClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); Assert.assertFalse("connection should have been closed", conn2.isOpen()); - mgr.releaseConnection(conn2, -1, null); + mgr.releaseConnection(conn2, null, -1, null); mgr.shutdown(); } @@ -603,7 +551,7 @@ public class TestPoolingConnManager extends LocalServerTestBase { SchemeRegistry registry = new SchemeRegistry(); registry.register(scheme); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(registry); + final PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(registry); mgr.setMaxTotal(1); HttpHost target = getServerHttp(); @@ -611,14 +559,15 @@ public class TestPoolingConnManager extends LocalServerTestBase { HttpContext context = new BasicHttpContext(); HttpParams params = new BasicHttpParams(); - final ManagedClientConnection conn = getConnection(mgr, route); + final HttpClientConnection conn = getConnection(mgr, route); final AtomicReference throwRef = new AtomicReference(); Thread abortingThread = new Thread(new Runnable() { public void run() { try { stallingSocketFactory.waitForState(); - conn.abortConnection(); + conn.shutdown(); + mgr.releaseConnection(conn, null, -1, null); connectLatch.countDown(); } catch (Throwable e) { throwRef.set(e); @@ -628,7 +577,7 @@ public class TestPoolingConnManager extends LocalServerTestBase { abortingThread.start(); try { - conn.open(route, context, params); + mgr.connect(conn, route.getTargetHost(), route.getLocalAddress(), context, params); Assert.fail("expected SocketException"); } catch(SocketException expected) {} @@ -647,79 +596,15 @@ public class TestPoolingConnManager extends LocalServerTestBase { Assert.assertEquals(1, localServer.getAcceptedConnectionCount()); // the connection is expected to be released back to the manager - ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); + HttpClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); Assert.assertFalse("connection should have been closed", conn2.isOpen()); - mgr.releaseConnection(conn2, -1, null); + mgr.releaseConnection(conn2, null, -1, null); mgr.shutdown(); } - @Test - public void testAbortAfterOperatorOpen() throws Exception { - final CountDownLatch connectLatch = new CountDownLatch(1); - final AtomicReference operatorRef = new AtomicReference(); + static class LatchSupport { - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager() { - @Override - protected ClientConnectionOperator createConnectionOperator( - SchemeRegistry schreg) { - operatorRef.set(new StallingOperator(connectLatch, WaitPolicy.AFTER_OPEN, super.createConnectionOperator(schreg))); - return operatorRef.get(); - } - }; - mgr.setMaxTotal(1); - Assert.assertNotNull(operatorRef.get()); - - HttpHost target = getServerHttp(); - HttpRoute route = new HttpRoute(target, null, false); - HttpContext context = new BasicHttpContext(); - HttpParams params = new BasicHttpParams(); - - final ManagedClientConnection conn = getConnection(mgr, route); - - final AtomicReference throwRef = new AtomicReference(); - Thread abortingThread = new Thread(new Runnable() { - public void run() { - try { - operatorRef.get().waitForState(); - conn.abortConnection(); - connectLatch.countDown(); - } catch (Throwable e) { - throwRef.set(e); - } - } - }); - abortingThread.start(); - - try { - conn.open(route, context, params); - Assert.fail("expected exception"); - } catch(IOException iox) { - } - - abortingThread.join(5000); - if(throwRef.get() != null) - throw new RuntimeException(throwRef.get()); - - Assert.assertFalse(conn.isOpen()); - // Give the server a bit of time to accept the connection, but - // ensure that it can accept it. - for(int i = 0; i < 10; i++) { - if(localServer.getAcceptedConnectionCount() == 1) - break; - Thread.sleep(100); - } - Assert.assertEquals(1, localServer.getAcceptedConnectionCount()); - - // the connection is expected to be released back to the manager - ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS); - Assert.assertFalse("connection should have been closed", conn2.isOpen()); - - mgr.releaseConnection(conn2, -1, null); - mgr.shutdown(); - } - - private static class LatchSupport { private final CountDownLatch continueLatch; private final CountDownLatch waitLatch = new CountDownLatch(1); protected final WaitPolicy waitPolicy; @@ -745,34 +630,6 @@ public class TestPoolingConnManager extends LocalServerTestBase { } } - private static class StallingOperator extends LatchSupport implements ClientConnectionOperator { - private final ClientConnectionOperator delegate; - - public StallingOperator(CountDownLatch continueLatch, - WaitPolicy waitPolicy, ClientConnectionOperator delegate) { - super(continueLatch, waitPolicy); - this.delegate = delegate; - } - - public OperatedClientConnection createConnection() { - return delegate.createConnection(); - } - - public void openConnection(OperatedClientConnection conn, - HttpHost target, InetAddress local, HttpContext context, - HttpParams params) throws IOException { - delegate.openConnection(conn, target, local, context, params); - if(waitPolicy == WaitPolicy.AFTER_OPEN) - latch(); - } - - public void updateSecureConnection(OperatedClientConnection conn, - HttpHost target, HttpContext context, HttpParams params) - throws IOException { - delegate.updateSecureConnection(conn, target, context, params); - } - } - private static class StallingSocketFactory extends LatchSupport implements SchemeSocketFactory { private final SchemeSocketFactory delegate; diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestContentCodings.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestContentCodings.java index ad0f28df3..2877d3ab9 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestContentCodings.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestContentCodings.java @@ -53,7 +53,7 @@ import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestHandler; import org.apache.http.util.EntityUtils; @@ -181,7 +181,7 @@ public class TestContentCodings extends IntegrationTestBase { */ int clients = 100; - PoolingClientConnectionManager cm = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(clients); this.httpclient = new HttpClientBuilder().setConnectionManager(cm).build(); diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java similarity index 93% rename from httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java rename to httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java index b0018059d..684f4f40f 100644 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestIdleConnectionEviction.java @@ -25,7 +25,7 @@ * */ -package org.apache.http.impl.conn; +package org.apache.http.impl.client.integration; import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; @@ -36,8 +36,9 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.localserver.LocalServerTestBase; import org.apache.http.localserver.LocalTestServer; import org.apache.http.util.EntityUtils; @@ -55,7 +56,7 @@ public class TestIdleConnectionEviction extends LocalServerTestBase { @Test public void testIdleConnectionEviction() throws Exception { - PoolingClientConnectionManager cm = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setDefaultMaxPerRoute(10); cm.setMaxTotal(50); @@ -131,10 +132,10 @@ public class TestIdleConnectionEviction extends LocalServerTestBase { public static class IdleConnectionMonitor extends Thread { - private final ClientConnectionManager cm; + private final HttpClientConnectionManager cm; private volatile boolean shutdown; - public IdleConnectionMonitor(final ClientConnectionManager cm) { + public IdleConnectionMonitor(final HttpClientConnectionManager cm) { super(); this.cm = cm; setDaemon(true); diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestStatefulConnManagement.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestStatefulConnManagement.java index 0125f5f24..12cc8998b 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestStatefulConnManagement.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestStatefulConnManagement.java @@ -27,6 +27,7 @@ package org.apache.http.impl.client.integration; import java.io.IOException; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; @@ -35,10 +36,9 @@ import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.UserTokenHandler; import org.apache.http.client.methods.HttpGet; -import org.apache.http.conn.ManagedClientConnection; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; @@ -91,14 +91,14 @@ public class TestStatefulConnManagement extends IntegrationTestBase { HttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 10); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(workerCount); mgr.setDefaultMaxPerRoute(workerCount); UserTokenHandler userTokenHandler = new UserTokenHandler() { public Object getUserToken(final HttpContext context) { - Integer id = (Integer) context.getAttribute("user"); + String id = (String) context.getAttribute("user"); return id; } @@ -113,9 +113,10 @@ public class TestStatefulConnManagement extends IntegrationTestBase { HttpWorker[] workers = new HttpWorker[workerCount]; for (int i = 0; i < contexts.length; i++) { HttpContext context = new BasicHttpContext(); - context.setAttribute("user", Integer.valueOf(i)); contexts[i] = context; - workers[i] = new HttpWorker(context, requestCount, target, this.httpclient); + workers[i] = new HttpWorker( + "user" + i, + context, requestCount, target, this.httpclient); } for (int i = 0; i < workers.length; i++) { @@ -134,12 +135,12 @@ public class TestStatefulConnManagement extends IntegrationTestBase { for (int i = 0; i < contexts.length; i++) { HttpContext context = contexts[i]; - Integer id = (Integer) context.getAttribute("user"); + String uid = (String) context.getAttribute("user"); for (int r = 0; r < requestCount; r++) { - Integer state = (Integer) context.getAttribute("r" + r); + String state = (String) context.getAttribute("r" + r); Assert.assertNotNull(state); - Assert.assertEquals(id, state); + Assert.assertEquals(uid, state); } } @@ -147,6 +148,7 @@ public class TestStatefulConnManagement extends IntegrationTestBase { static class HttpWorker extends Thread { + private final String uid; private final HttpContext context; private final int requestCount; private final HttpHost target; @@ -156,11 +158,13 @@ public class TestStatefulConnManagement extends IntegrationTestBase { private volatile int count; public HttpWorker( + final String uid, final HttpContext context, int requestCount, final HttpHost target, final HttpClient httpclient) { super(); + this.uid = uid; this.context = context; this.requestCount = requestCount; this.target = target; @@ -179,6 +183,7 @@ public class TestStatefulConnManagement extends IntegrationTestBase { @Override public void run() { try { + this.context.setAttribute("user", this.uid); for (int r = 0; r < this.requestCount; r++) { HttpGet httpget = new HttpGet("/"); HttpResponse response = this.httpclient.execute( @@ -187,11 +192,15 @@ public class TestStatefulConnManagement extends IntegrationTestBase { this.context); this.count++; - ManagedClientConnection conn = (ManagedClientConnection) this.context.getAttribute( + HttpClientConnection conn = (HttpClientConnection) this.context.getAttribute( ExecutionContext.HTTP_CONNECTION); - - this.context.setAttribute("r" + r, conn.getState()); - + HttpContext connContext = (HttpContext) conn; + String connuid = (String) connContext.getAttribute("user"); + if (connuid == null) { + connContext.setAttribute("user", this.uid); + connuid = this.uid; + } + this.context.setAttribute("r" + r, connuid); EntityUtils.consume(response.getEntity()); } @@ -214,7 +223,7 @@ public class TestStatefulConnManagement extends IntegrationTestBase { this.localServer.register("*", new SimpleService()); // We build a client with 2 max active // connections, and 2 max per route. - PoolingClientConnectionManager connMngr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager connMngr = new PoolingHttpClientConnectionManager(); connMngr.setMaxTotal(maxConn); connMngr.setDefaultMaxPerRoute(maxConn); diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java deleted file mode 100644 index 82190b935..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn; - -import java.util.concurrent.TimeUnit; - -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponse; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ClientConnectionRequest; -import org.apache.http.conn.ManagedClientConnection; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpProcessor; -import org.apache.http.protocol.HttpRequestExecutor; -import org.apache.http.protocol.ImmutableHttpProcessor; -import org.apache.http.protocol.RequestConnControl; -import org.apache.http.protocol.RequestContent; -import org.apache.http.util.EntityUtils; - -class ExecReqThread extends Thread { - - private final HttpRequest request; - private final HttpRoute route; - private final ClientConnectionManager connman; - private final long timeout; - - private volatile Exception exception; - private volatile HttpResponse response; - private volatile byte[] response_data; - - public ExecReqThread( - HttpRequest request, - HttpRoute route, - ClientConnectionManager mgr, - long timeout) { - super(); - this.request = request; - this.route = route; - this.connman = mgr; - this.timeout = timeout; - } - - public Exception getException() { - return this.exception; - } - - public HttpResponse getResponse() { - return this.response; - } - - public byte[] getResponseData() { - return this.response_data; - } - - @Override - public void run() { - try { - - HttpProcessor processor = new ImmutableHttpProcessor( - new HttpRequestInterceptor[] { new RequestContent(), new RequestConnControl() }); - HttpRequestExecutor executor = new HttpRequestExecutor(); - HttpContext context = new BasicHttpContext(); - HttpParams params = new BasicHttpParams(); - - ClientConnectionRequest connRequest = this.connman.requestConnection(this.route, null); - ManagedClientConnection conn = connRequest.getConnection(this.timeout, TimeUnit.MILLISECONDS); - try { - context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.route.getTargetHost()); - context.setAttribute(ClientContext.ROUTE, this.route); - context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); - - conn.open(this.route, context, params); - - executor.preProcess(this.request, processor, context); - - this.response = executor.execute(this.request, conn, context); - if (this.response.getEntity() != null) { - this.response_data = EntityUtils.toByteArray(this.response.getEntity()); - } - } finally { - this.connman.releaseConnection(conn, -1, null); - } - } catch (Exception ex) { - this.exception = ex; - } - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java deleted file mode 100644 index 84178b426..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn; - -import java.util.concurrent.TimeUnit; - -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.HttpVersion; -import org.apache.http.conn.ManagedClientConnection; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.localserver.LocalServerTestBase; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.HttpProcessor; -import org.apache.http.protocol.HttpRequestExecutor; -import org.apache.http.protocol.ImmutableHttpProcessor; -import org.apache.http.protocol.RequestConnControl; -import org.apache.http.protocol.RequestContent; -import org.apache.http.util.EntityUtils; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestBasicConnManager extends LocalServerTestBase { - - @Before - public void setup() throws Exception { - startServer(); - } - - /** - * Tests that SCM can still connect to the same host after - * a connection was aborted. - */ - @Test - public void testOpenAfterAbort() throws Exception { - BasicClientConnectionManager mgr = new BasicClientConnectionManager(); - - HttpHost target = getServerHttp(); - HttpRoute route = new HttpRoute(target, null, false); - HttpContext context = new BasicHttpContext(); - HttpParams params = new BasicHttpParams(); - - ManagedClientConnection conn = mgr.getConnection(route, null); - conn.abortConnection(); - - conn = mgr.getConnection(route, null); - Assert.assertFalse("connection should have been closed", conn.isOpen()); - conn.open(route, context, params); - - mgr.releaseConnection(conn, -1, null); - mgr.shutdown(); - } - - /** - * Tests releasing with time limits. - */ - @Test - public void testReleaseConnectionWithTimeLimits() throws Exception { - BasicClientConnectionManager mgr = new BasicClientConnectionManager(); - - HttpHost target = getServerHttp(); - HttpRoute route = new HttpRoute(target, null, false); - HttpContext context = new BasicHttpContext(); - HttpParams params = new BasicHttpParams(); - int rsplen = 8; - String uri = "/random/" + rsplen; - - HttpRequest request = new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1); - - ManagedClientConnection conn = mgr.getConnection(route, null); - conn.open(route, context, params); - - // a new context is created for each test case, no need to reset - context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); - context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target); - - HttpProcessor httpProcessor = new ImmutableHttpProcessor( - new HttpRequestInterceptor[] { new RequestContent(), new RequestConnControl() }); - - HttpRequestExecutor exec = new HttpRequestExecutor(); - exec.preProcess(request, httpProcessor, context); - HttpResponse response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in first response", - HttpStatus.SC_OK, - response.getStatusLine().getStatusCode()); - byte[] data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of first response entity", - rsplen, data.length); - // ignore data, but it must be read - - // release connection without marking for re-use - // expect the next connection obtained to be closed - mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS); - conn = mgr.getConnection(route, null); - Assert.assertFalse("connection should have been closed", conn.isOpen()); - - // repeat the communication, no need to prepare the request again - conn.open(route, context, params); - context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in second response", - HttpStatus.SC_OK, - response.getStatusLine().getStatusCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of second response entity", - rsplen, data.length); - // ignore data, but it must be read - - // release connection after marking it for re-use - // expect the next connection obtained to be open - conn.markReusable(); - mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS); - conn = mgr.getConnection(route, null); - Assert.assertTrue("connection should have been open", conn.isOpen()); - - // repeat the communication, no need to prepare the request again - context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in third response", - HttpStatus.SC_OK, - response.getStatusLine().getStatusCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of third response entity", - rsplen, data.length); - // ignore data, but it must be read - - conn.markReusable(); - mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS); - Thread.sleep(150); - conn = mgr.getConnection(route, null); - Assert.assertTrue("connection should have been closed", !conn.isOpen()); - - // repeat the communication, no need to prepare the request again - conn.open(route, context, params); - context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); - response = exec.execute(request, conn, context); - - Assert.assertEquals("wrong status in third response", - HttpStatus.SC_OK, - response.getStatusLine().getStatusCode()); - data = EntityUtils.toByteArray(response.getEntity()); - Assert.assertEquals("wrong length of fourth response entity", - rsplen, data.length); - // ignore data, but it must be read - - mgr.shutdown(); - } - - @Test - public void testCloseExpiredConnections() throws Exception { - BasicClientConnectionManager mgr = new BasicClientConnectionManager(); - - HttpHost target = getServerHttp(); - HttpRoute route = new HttpRoute(target, null, false); - HttpContext context = new BasicHttpContext(); - HttpParams params = new BasicHttpParams(); - - ManagedClientConnection conn = mgr.getConnection(route, null); - conn.open(route, context, params); - conn.markReusable(); - mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS); - - mgr.closeExpiredConnections(); - - conn = mgr.getConnection(route, null); - Assert.assertTrue(conn.isOpen()); - mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS); - - Thread.sleep(150); - mgr.closeExpiredConnections(); - conn = mgr.getConnection(route, null); - Assert.assertFalse(conn.isOpen()); - - mgr.shutdown(); - } - - @Test(expected=IllegalStateException.class) - public void testAlreadyLeased() throws Exception { - BasicClientConnectionManager mgr = new BasicClientConnectionManager(); - - HttpHost target = getServerHttp(); - HttpRoute route = new HttpRoute(target, null, false); - - ManagedClientConnection conn = mgr.getConnection(route, null); - mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS); - - mgr.getConnection(route, null); - mgr.getConnection(route, null); - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java new file mode 100644 index 000000000..3b23ca7a7 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicHttpClientConnectionManager.java @@ -0,0 +1,308 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.net.Socket; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpHost; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.HttpConnectionFactory; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.scheme.SchemeSocketFactory; +import org.apache.http.params.HttpParams; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestBasicHttpClientConnectionManager { + + private DefaultClientConnection conn; + private HttpConnectionFactory connFactory; + private Socket socket; + private SchemeSocketFactory plainSocketFactory; + private SchemeRegistry schemeRegistry; + private DnsResolver dnsResolver; + private BasicHttpClientConnectionManager mgr; + + @SuppressWarnings("unchecked") + @Before + public void setup() throws Exception { + conn = Mockito.mock(DefaultClientConnection.class); + connFactory = Mockito.mock(HttpConnectionFactory.class); + Mockito.when(connFactory.create()).thenReturn(conn); + socket = Mockito.mock(Socket.class); + plainSocketFactory = Mockito.mock(SchemeSocketFactory.class); + Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); + + Scheme http = new Scheme("http", 80, plainSocketFactory); + schemeRegistry = new SchemeRegistry(); + schemeRegistry.register(http); + + dnsResolver = Mockito.mock(DnsResolver.class); + + mgr = new BasicHttpClientConnectionManager(schemeRegistry, dnsResolver, connFactory); + } + + @Test + public void testLeaseReleaseNonReusable() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + Assert.assertFalse(conn1.isOpen()); + + mgr.releaseConnection(conn1, null, 100, TimeUnit.MILLISECONDS); + + Assert.assertNull(mgr.getRoute()); + Assert.assertNull(mgr.getState()); + + ConnectionRequest connRequest2 = mgr.requestConnection(route, null); + HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn2); + Assert.assertFalse(conn2.isOpen()); + + Mockito.verify(connFactory, Mockito.times(2)).create(); + } + + @Test + public void testLeaseReleaseReusable() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); + + mgr.releaseConnection(conn1, null, 10000, TimeUnit.MILLISECONDS); + + Assert.assertEquals(route, mgr.getRoute()); + Assert.assertEquals(null, mgr.getState()); + + ConnectionRequest connRequest2 = mgr.requestConnection(route, null); + HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn2); + Assert.assertTrue(conn2.isOpen()); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + } + + @Test + public void testLeaseReleaseReusableWithState() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, "some state"); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); + + mgr.releaseConnection(conn1, "some other state", 10000, TimeUnit.MILLISECONDS); + + Assert.assertEquals(route, mgr.getRoute()); + Assert.assertEquals("some other state", mgr.getState()); + + ConnectionRequest connRequest2 = mgr.requestConnection(route, "some other state"); + HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn2); + Assert.assertTrue(conn2.isOpen()); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + } + + @Test + public void testLeaseDifferentRoute() throws Exception { + HttpHost target1 = new HttpHost("somehost"); + HttpRoute route1 = new HttpRoute(target1); + ConnectionRequest connRequest1 = mgr.requestConnection(route1, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); + + mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + + Assert.assertEquals(route1, mgr.getRoute()); + Assert.assertEquals(null, mgr.getState()); + + HttpHost target2 = new HttpHost("otherhost"); + HttpRoute route2 = new HttpRoute(target2); + ConnectionRequest connRequest2 = mgr.requestConnection(route2, null); + HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn2); + Assert.assertFalse(conn2.isOpen()); + + Mockito.verify(conn).close(); + Mockito.verify(connFactory, Mockito.times(2)).create(); + } + + @Test + public void testLeaseExpired() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); + + mgr.releaseConnection(conn1, null, 10, TimeUnit.MILLISECONDS); + + Assert.assertEquals(route, mgr.getRoute()); + Assert.assertEquals(null, mgr.getState()); + + Thread.sleep(50); + + ConnectionRequest connRequest2 = mgr.requestConnection(route, null); + HttpClientConnection conn2 = connRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn2); + Assert.assertFalse(conn2.isOpen()); + + Mockito.verify(conn).close(); + Mockito.verify(connFactory, Mockito.times(2)).create(); + } + + @Test(expected=IllegalArgumentException.class) + public void testLeaseInvalidArg() throws Exception { + mgr.requestConnection(null, null); + } + + @Test(expected=IllegalArgumentException.class) + public void testReleaseInvalidArg() throws Exception { + mgr.releaseConnection(null, null, 0, TimeUnit.MILLISECONDS); + } + + @Test(expected=IllegalArgumentException.class) + public void testReleaseAnotherConnection() throws Exception { + HttpClientConnection wrongCon = Mockito.mock(HttpClientConnection.class); + mgr.releaseConnection(wrongCon, null, 0, TimeUnit.MILLISECONDS); + } + + @Test + public void testShutdown() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); + + mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + + mgr.shutdown(); + + Mockito.verify(conn, Mockito.times(1)).shutdown(); + + try { + ConnectionRequest connRequest2 = mgr.requestConnection(route, null); + connRequest2.get(0, TimeUnit.MILLISECONDS); + Assert.fail("IllegalStateException expected"); + } catch (IllegalStateException ex) { + } + } + + @Test + public void testCloseExpired() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); + + mgr.releaseConnection(conn1, null, 10, TimeUnit.MILLISECONDS); + + Assert.assertEquals(route, mgr.getRoute()); + Assert.assertEquals(null, mgr.getState()); + + Thread.sleep(50); + + mgr.closeExpiredConnections(); + + Mockito.verify(conn).close(); + } + + @Test + public void testCloseIdle() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + + Mockito.verify(connFactory, Mockito.times(1)).create(); + + Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); + + mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + + Assert.assertEquals(route, mgr.getRoute()); + Assert.assertEquals(null, mgr.getState()); + + Thread.sleep(100); + + mgr.closeIdleConnections(50, TimeUnit.MILLISECONDS); + + Mockito.verify(conn).close(); + } + + @Test(expected=IllegalStateException.class) + public void testAlreadyLeased() throws Exception { + HttpHost target = new HttpHost("somehost"); + HttpRoute route = new HttpRoute(target); + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(0, TimeUnit.MILLISECONDS); + Assert.assertNotNull(conn1); + mgr.releaseConnection(conn1, null, 100, TimeUnit.MILLISECONDS); + + mgr.getConnection(route, null); + mgr.getConnection(route, null); + } + +} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java index 26c0763c5..e3fb8260f 100644 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java @@ -37,6 +37,7 @@ import org.apache.http.conn.DnsResolver; import org.junit.Assert; import org.junit.Test; +@Deprecated public class TestDefaultClientConnectOperator { @Test diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java new file mode 100644 index 000000000..78c19ddc9 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionManagerBase.java @@ -0,0 +1,194 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.net.Socket; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpClientConnection; +import org.apache.http.HttpHost; +import org.apache.http.conn.ConnectionPoolTimeoutException; +import org.apache.http.conn.ConnectionRequest; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.scheme.SchemeSocketFactory; +import org.apache.http.params.HttpParams; +import org.apache.http.pool.ConnPool; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestHttpClientConnectionManagerBase { + + private DefaultClientConnection conn; + private Socket socket; + private SchemeSocketFactory plainSocketFactory; + private SchemeRegistry schemeRegistry; + private DnsResolver dnsResolver; + private Future future; + private ConnPool pool; + private HttpClientConnectionManagerBase mgr; + + @SuppressWarnings("unchecked") + @Before + public void setup() throws Exception { + conn = Mockito.mock(DefaultClientConnection.class); + socket = Mockito.mock(Socket.class); + plainSocketFactory = Mockito.mock(SchemeSocketFactory.class); + Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); + dnsResolver = Mockito.mock(DnsResolver.class); + + Scheme http = new Scheme("http", 80, plainSocketFactory); + schemeRegistry = new SchemeRegistry(); + schemeRegistry.register(http); + + pool = Mockito.mock(ConnPool.class); + future = Mockito.mock(Future.class); + mgr = new HttpClientConnectionManagerBase(pool, schemeRegistry, dnsResolver) { + + public void closeIdleConnections(long idletime, TimeUnit tunit) { + } + + public void closeExpiredConnections() { + } + + public void shutdown() { + } + + }; + } + + @Test + public void testLeaseRelease() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + + CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS); + + Mockito.when(future.isCancelled()).thenReturn(false); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(conn1); + Assert.assertFalse(conn1.isOpen()); + Assert.assertNotSame(conn, conn1); + + mgr.releaseConnection(conn1, null, 0, TimeUnit.MILLISECONDS); + + Mockito.verify(pool).release(entry, false); + } + + @Test(expected=InterruptedException.class) + public void testLeaseFutureCancelled() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + + CPoolEntry entry = new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS); + + Mockito.when(future.isCancelled()).thenReturn(true); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + connRequest1.get(1, TimeUnit.SECONDS); + } + + @Test(expected=ConnectionPoolTimeoutException.class) + public void testLeaseFutureTimeout() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + + Mockito.when(future.isCancelled()).thenReturn(true); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenThrow(new TimeoutException()); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + connRequest1.get(1, TimeUnit.SECONDS); + } + + @Test + public void testReleaseReusable() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + + CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS)); + + Mockito.when(future.isCancelled()).thenReturn(false); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + Mockito.when(conn.isOpen()).thenReturn(true); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(conn1); + Assert.assertTrue(conn1.isOpen()); + + mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); + + Mockito.verify(pool).release(entry, true); + Mockito.verify(entry).setState("some state"); + Mockito.verify(entry).updateExpiry(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)); + } + + @Test + public void testReleaseNonReusable() throws Exception { + HttpHost target = new HttpHost("localhost"); + HttpRoute route = new HttpRoute(target); + + CPoolEntry entry = Mockito.spy(new CPoolEntry(LogFactory.getLog(getClass()), "id", route, conn, + -1, TimeUnit.MILLISECONDS)); + + Mockito.when(future.isCancelled()).thenReturn(false); + Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); + Mockito.when(pool.lease(route, null, null)).thenReturn(future); + Mockito.when(conn.isOpen()).thenReturn(false); + + ConnectionRequest connRequest1 = mgr.requestConnection(route, null); + HttpClientConnection conn1 = connRequest1.get(1, TimeUnit.SECONDS); + Assert.assertNotNull(conn1); + Assert.assertFalse(conn1.isOpen()); + + mgr.releaseConnection(conn1, "some state", 0, TimeUnit.MILLISECONDS); + + Mockito.verify(pool).release(entry, false); + Mockito.verify(entry, Mockito.never()).setState(Mockito.anyObject()); + Mockito.verify(entry, Mockito.never()).updateExpiry(Mockito.anyLong(), Mockito.eq(TimeUnit.MILLISECONDS)); + } + +} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java new file mode 100644 index 000000000..a5776aac0 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestHttpClientConnectionOperator.java @@ -0,0 +1,179 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.conn; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +import org.apache.http.HttpHost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.DnsResolver; +import org.apache.http.conn.HttpInetSocketAddress; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeLayeredSocketFactory; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.scheme.SchemeSocketFactory; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.HttpContext; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestHttpClientConnectionOperator { + + private DefaultClientConnection conn; + private Socket socket; + private SchemeSocketFactory plainSocketFactory; + private SchemeLayeredSocketFactory sslSocketFactory; + private SchemeRegistry schemeRegistry; + private DnsResolver dnsResolver; + private HttpClientConnectionOperator connectionOperator; + + @Before + public void setup() throws Exception { + conn = Mockito.mock(DefaultClientConnection.class); + socket = Mockito.mock(Socket.class); + plainSocketFactory = Mockito.mock(SchemeSocketFactory.class); + sslSocketFactory = Mockito.mock(SchemeLayeredSocketFactory.class); + Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); + Mockito.when(sslSocketFactory.createSocket(Mockito.any())).thenReturn(socket); + dnsResolver = Mockito.mock(DnsResolver.class); + + Scheme http = new Scheme("http", 80, plainSocketFactory); + Scheme https = new Scheme("https", 443, sslSocketFactory); + schemeRegistry = new SchemeRegistry(); + schemeRegistry.register(http); + schemeRegistry.register(https); + + connectionOperator = new HttpClientConnectionOperator(schemeRegistry, dnsResolver); + } + + @Test + public void testConnect() throws Exception { + HttpContext context = new BasicHttpContext(); + HttpParams params = new BasicHttpParams(); + HttpHost host = new HttpHost("somehost"); + InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); + InetAddress ip1 = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); + InetAddress ip2 = InetAddress.getByAddress(new byte[] {127, 0, 0, 2}); + + Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); + Mockito.when(plainSocketFactory.connectSocket( + Mockito.any(), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenReturn(socket); + + connectionOperator.connect(conn, host, local, context, params); + + Mockito.verify(plainSocketFactory).connectSocket(socket, + new InetSocketAddress(ip1, 80), + new InetSocketAddress(local, 0), params); + Mockito.verify(conn).opening(socket, host); + Mockito.verify(conn).openCompleted(false, params); + } + + @Test(expected=ConnectTimeoutException.class) + public void testConnectFailure() throws Exception { + HttpContext context = new BasicHttpContext(); + HttpParams params = new BasicHttpParams(); + HttpHost host = new HttpHost("somehost"); + InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); + InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); + InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); + + Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); + Mockito.when(plainSocketFactory.connectSocket( + Mockito.any(), + Mockito.any(), + Mockito.any(), + Mockito.any())).thenThrow(new ConnectTimeoutException()); + + connectionOperator.connect(conn, host, local, context, params); + } + + @Test + public void testConnectFailover() throws Exception { + HttpContext context = new BasicHttpContext(); + HttpParams params = new BasicHttpParams(); + HttpHost host = new HttpHost("somehost"); + InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); + InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); + InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); + + Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); + Mockito.when(plainSocketFactory.connectSocket( + Mockito.any(), + Mockito.eq(new HttpInetSocketAddress(host, ip1, 80)), + Mockito.any(), + Mockito.any())).thenThrow(new ConnectTimeoutException()); + Mockito.when(plainSocketFactory.connectSocket( + Mockito.any(), + Mockito.eq(new HttpInetSocketAddress(host, ip2, 80)), + Mockito.any(), + Mockito.any())).thenReturn(socket); + + connectionOperator.connect(conn, host, local, context, params); + + Mockito.verify(plainSocketFactory).connectSocket(socket, + new HttpInetSocketAddress(host, ip2, 80), + new InetSocketAddress(local, 0), params); + Mockito.verify(conn, Mockito.times(2)).opening(socket, host); + Mockito.verify(conn).openCompleted(false, params); + } + + @Test + public void testUpgrade() throws Exception { + HttpContext context = new BasicHttpContext(); + HttpParams params = new BasicHttpParams(); + HttpHost host = new HttpHost("somehost", -1, "https"); + + Mockito.when(sslSocketFactory.createLayeredSocket( + Mockito.any(), + Mockito.eq("somehost"), + Mockito.eq(443), + Mockito.any())).thenReturn(socket); + + connectionOperator.upgrade(conn, host, context, params); + + Mockito.verify(conn).update(socket, host, false, params); + } + + @Test(expected=IllegalArgumentException.class) + public void testUpgradeNonLayeringScheme() throws Exception { + HttpContext context = new BasicHttpContext(); + HttpParams params = new BasicHttpParams(); + HttpHost host = new HttpHost("somehost", -1, "http"); + + connectionOperator.upgrade(conn, host, context, params); + } + +} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManagerNoServer.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java similarity index 78% rename from httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManagerNoServer.java rename to httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java index 1183a6802..e299a2516 100644 --- a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManagerNoServer.java +++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingHttpClientConnectionManager.java @@ -29,76 +29,55 @@ package org.apache.http.impl.conn; import java.util.concurrent.TimeUnit; +import org.apache.http.HttpClientConnection; import org.apache.http.HttpHost; -import org.apache.http.HttpVersion; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.ClientConnectionRequest; +import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.ManagedClientConnection; +import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.impl.conn.tsccm.GetConnThread; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; /** - * Tests for PoolingClientConnectionManager that do not require a server to - * communicate with. + * {@link PoolingHttpClientConnectionManager} tests. */ -public class TestPoolingConnManagerNoServer { +public class TestPoolingHttpClientConnectionManager { - private static ManagedClientConnection getConnection( - final ClientConnectionManager mgr, + private static HttpClientConnection getConnection( + final HttpClientConnectionManager mgr, final HttpRoute route, long timeout, TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException { - ClientConnectionRequest connRequest = mgr.requestConnection(route, null); - return connRequest.getConnection(timeout, unit); + ConnectionRequest connRequest = mgr.requestConnection(route, null); + return connRequest.get(timeout, unit); } - private static ManagedClientConnection getConnection( - final ClientConnectionManager mgr, + private static HttpClientConnection getConnection( + final HttpClientConnectionManager mgr, final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException { - ClientConnectionRequest connRequest = mgr.requestConnection(route, null); - return connRequest.getConnection(0, null); - } - - /** - * Instantiates default parameters. - * - * @return the default parameters - */ - public HttpParams createDefaultParams() { - - HttpParams params = new BasicHttpParams(); - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - HttpProtocolParams.setUseExpectContinue(params, false); - - return params; + ConnectionRequest connRequest = mgr.requestConnection(route, null); + return connRequest.get(0, null); } @Test(expected=IllegalArgumentException.class) public void testIllegalConstructor() { - new PoolingClientConnectionManager(null); + new PoolingHttpClientConnectionManager(null); } @Test(expected=IllegalArgumentException.class) public void testGetConnection() throws InterruptedException, ConnectionPoolTimeoutException { - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); - ManagedClientConnection conn = getConnection(mgr, route); + HttpClientConnection conn = getConnection(mgr, route); Assert.assertNotNull(conn); - Assert.assertNull(conn.getRoute()); Assert.assertFalse(conn.isOpen()); - mgr.releaseConnection(conn, -1, null); + mgr.releaseConnection(conn, null, -1, null); try { getConnection(mgr, null); @@ -113,7 +92,7 @@ public class TestPoolingConnManagerNoServer { public void testMaxConnTotal() throws InterruptedException, ConnectionPoolTimeoutException { - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(2); mgr.setDefaultMaxPerRoute(1); @@ -122,9 +101,9 @@ public class TestPoolingConnManagerNoServer { HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http"); HttpRoute route2 = new HttpRoute(target2, null, false); - ManagedClientConnection conn1 = getConnection(mgr, route1); + HttpClientConnection conn1 = getConnection(mgr, route1); Assert.assertNotNull(conn1); - ManagedClientConnection conn2 = getConnection(mgr, route2); + HttpClientConnection conn2 = getConnection(mgr, route2); Assert.assertNotNull(conn2); try { @@ -136,7 +115,7 @@ public class TestPoolingConnManagerNoServer { } // release one of the connections - mgr.releaseConnection(conn2, -1, null); + mgr.releaseConnection(conn2, null, -1, null); conn2 = null; // there should be a connection available now @@ -159,20 +138,20 @@ public class TestPoolingConnManagerNoServer { HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http"); HttpRoute route3 = new HttpRoute(target3, null, false); - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(100); mgr.setDefaultMaxPerRoute(1); mgr.setMaxPerRoute(route2, 2); mgr.setMaxPerRoute(route3, 3); // route 3, limit 3 - ManagedClientConnection conn1 = + HttpClientConnection conn1 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn1); - ManagedClientConnection conn2 = + HttpClientConnection conn2 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn2); - ManagedClientConnection conn3 = + HttpClientConnection conn3 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn3); try { @@ -203,18 +182,16 @@ public class TestPoolingConnManagerNoServer { } catch (ConnectionPoolTimeoutException e) { // expected } - - // check releaseConnection with invalid arguments try { - mgr.releaseConnection(null, -1, null); + mgr.releaseConnection(null, null, -1, null); Assert.fail("null connection adapter not detected"); } catch (IllegalArgumentException iax) { // expected } try { - ManagedClientConnection conn = Mockito.mock(ManagedClientConnection.class); - mgr.releaseConnection(conn, -1, null); + HttpClientConnection conn = Mockito.mock(HttpClientConnection.class); + mgr.releaseConnection(conn, null, -1, null); Assert.fail("foreign connection adapter not detected"); } catch (IllegalArgumentException iax) { // expected @@ -226,7 +203,7 @@ public class TestPoolingConnManagerNoServer { @Test public void testReleaseConnection() throws Exception { - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(3); mgr.setDefaultMaxPerRoute(1); @@ -238,11 +215,11 @@ public class TestPoolingConnManagerNoServer { HttpRoute route3 = new HttpRoute(target3, null, false); // the first three allocations should pass - ManagedClientConnection conn1 = + HttpClientConnection conn1 = getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); - ManagedClientConnection conn2 = + HttpClientConnection conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS); - ManagedClientConnection conn3 = + HttpClientConnection conn3 = getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull(conn1); Assert.assertNotNull(conn2); @@ -270,7 +247,7 @@ public class TestPoolingConnManagerNoServer { } // now release one and check that exactly that one can be obtained then - mgr.releaseConnection(conn2, -1, null); + mgr.releaseConnection(conn2, null, -1, null); conn2 = null; try { getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS); @@ -292,26 +269,17 @@ public class TestPoolingConnManagerNoServer { } @Test - public void testDeleteClosedConnections() - throws InterruptedException, ConnectionPoolTimeoutException { - - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + public void testDeleteClosedConnections() throws Exception { + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); HttpHost target = new HttpHost("www.test.invalid", 80, "http"); HttpRoute route = new HttpRoute(target, null, false); - ManagedClientConnection conn = getConnection(mgr, route); + HttpClientConnection conn = getConnection(mgr, route); Assert.assertEquals(1, mgr.getTotalStats().getLeased()); Assert.assertEquals(1, mgr.getStats(route).getLeased()); - conn.markReusable(); - mgr.releaseConnection(conn, -1, null); - - Assert.assertEquals(1, mgr.getTotalStats().getAvailable()); - Assert.assertEquals(1, mgr.getStats(route).getAvailable()); - - // this implicitly deletes them - mgr.closeIdleConnections(0L, TimeUnit.MILLISECONDS); + mgr.releaseConnection(conn, null, -1, null); Assert.assertEquals(0, mgr.getTotalStats().getAvailable()); Assert.assertEquals(0, mgr.getStats(route).getAvailable()); @@ -323,7 +291,7 @@ public class TestPoolingConnManagerNoServer { public void testShutdown() throws Exception { // 3.x: TestHttpConnectionManager.testShutdown - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(1); mgr.setDefaultMaxPerRoute(1); @@ -333,7 +301,7 @@ public class TestPoolingConnManagerNoServer { // get the only connection, then start an extra thread // on shutdown, the extra thread should get an exception - ManagedClientConnection conn = + HttpClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout gct.start(); @@ -345,7 +313,7 @@ public class TestPoolingConnManagerNoServer { // First release the connection. If the manager keeps working // despite the shutdown, this will deblock the extra thread. // The release itself should turn into a no-op, without exception. - mgr.releaseConnection(conn, -1, null); + mgr.releaseConnection(conn, null, -1, null); gct.join(10000); @@ -369,14 +337,14 @@ public class TestPoolingConnManagerNoServer { public void testInterruptThread() throws Exception { // 3.x: TestHttpConnectionManager.testWaitingThreadInterrupted - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(1); 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 = + HttpClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout gct.start(); @@ -402,7 +370,7 @@ public class TestPoolingConnManagerNoServer { // expected } - mgr.releaseConnection(conn, -1, null); + mgr.releaseConnection(conn, null, -1, null); // this time: no exception conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull("should have gotten a connection", conn); @@ -414,7 +382,7 @@ public class TestPoolingConnManagerNoServer { public void testReusePreference() throws Exception { // 3.x: TestHttpConnectionManager.testHostReusePreference - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(1); HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http"); @@ -423,7 +391,7 @@ public class TestPoolingConnManagerNoServer { HttpRoute route2 = new HttpRoute(target2, null, false); // get the only connection, then start two extra threads - ManagedClientConnection conn = + HttpClientConnection conn = getConnection(mgr, route1, 1L, TimeUnit.MILLISECONDS); GetConnThread gct1 = new GetConnThread(mgr, route1, 1000L); GetConnThread gct2 = new GetConnThread(mgr, route2, 1000L); @@ -438,7 +406,7 @@ public class TestPoolingConnManagerNoServer { // releasing the connection for route1 should deblock thread1 // the other thread gets a timeout - mgr.releaseConnection(conn, -1, null); + mgr.releaseConnection(conn, null, -1, null); gct1.join(10000); gct2.join(10000); @@ -453,20 +421,20 @@ public class TestPoolingConnManagerNoServer { @Test public void testAbortAfterRequestStarts() throws Exception { - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(1); 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 = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - ClientConnectionRequest request = mgr.requestConnection(route, null); + HttpClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); + ConnectionRequest request = mgr.requestConnection(route, null); GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout gct.start(); Thread.sleep(100); // give extra thread time to block - request.abortRequest(); + request.cancel(); gct.join(10000); Assert.assertNotNull("thread should have gotten an exception", @@ -483,7 +451,7 @@ public class TestPoolingConnManagerNoServer { // expected } - mgr.releaseConnection(conn, -1, null); + mgr.releaseConnection(conn, null, -1, null); // this time: no exception conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull("should have gotten a connection", conn); @@ -493,16 +461,16 @@ public class TestPoolingConnManagerNoServer { @Test public void testAbortBeforeRequestStarts() throws Exception { - PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(); + PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(); mgr.setMaxTotal(1); 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 = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); - ClientConnectionRequest request = mgr.requestConnection(route, null); - request.abortRequest(); + HttpClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS); + ConnectionRequest request = mgr.requestConnection(route, null); + request.cancel(); GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout gct.start(); @@ -523,7 +491,7 @@ public class TestPoolingConnManagerNoServer { // expected } - mgr.releaseConnection(conn, -1, null); + mgr.releaseConnection(conn, null, -1, null); // this time: no exception conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS); Assert.assertNotNull("should have gotten a connection", conn); @@ -531,4 +499,45 @@ public class TestPoolingConnManagerNoServer { mgr.shutdown(); } + public class GetConnThread extends Thread { + + private final ConnectionRequest connRequest; + private final long timeout; + + private volatile HttpClientConnection connection; + private volatile Exception exception; + + public GetConnThread( + final HttpClientConnectionManager mgr, + final HttpRoute route, long timeout) { + this(mgr.requestConnection(route, null), route, timeout); + } + + public GetConnThread( + final ConnectionRequest connRequest, + final HttpRoute route, long timeout) { + super(); + this.connRequest = connRequest; + this.timeout = timeout; + } + + @Override + public void run() { + try { + connection = connRequest.get(timeout, TimeUnit.MILLISECONDS); + } catch (Exception ex) { + exception = ex; + } + } + + public Throwable getException() { + return exception; + } + + public HttpClientConnection getConnection() { + return connection; + } + + } + } diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java deleted file mode 100644 index a6dd17de2..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn.tsccm; - - -import java.util.Date; -import java.util.concurrent.locks.Lock; - - -/** - * Thread to await something. - */ -@Deprecated -public class AwaitThread extends Thread { - - protected final WaitingThread wait_object; - protected final Lock wait_lock; - protected final Date wait_deadline; - - protected volatile boolean waiting; - protected volatile Throwable exception; - - - /** - * Creates a new thread. - * When this thread is started, it will wait on the argument object. - */ - public AwaitThread(WaitingThread where, Lock lck, Date deadline) { - - wait_object = where; - wait_lock = lck; - wait_deadline = deadline; - } - - - /** - * This method is executed when the thread is started. - */ - @Override - public void run() { - try { - wait_lock.lock(); - waiting = true; - wait_object.await(wait_deadline); - } catch (Throwable dart) { - exception = dart; - } finally { - waiting = false; - wait_lock.unlock(); - } - // terminate - } - - - public Throwable getException() { - return exception; - } - - public boolean isWaiting() { - try { - wait_lock.lock(); - return waiting; - } finally { - wait_lock.unlock(); - } - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/GetConnThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/GetConnThread.java deleted file mode 100644 index d2279a44c..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/GetConnThread.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn.tsccm; - -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; - - -/** - * Thread to get a connection from a connection manager. - * Used by connection manager tests. - * Code based on HttpClient 3.x class TestHttpConnectionManager. - */ -public class GetConnThread extends Thread { - - protected final HttpRoute conn_route; - protected final long conn_timeout; - protected final ClientConnectionRequest conn_request; - - protected volatile ManagedClientConnection connection; - protected volatile Exception exception; - - /** - * 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) { - this(mgr.requestConnection(route, null), 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_request.getConnection - (conn_timeout, TimeUnit.MILLISECONDS); - } catch (Exception ex) { - exception = ex; - } - // terminate - } - - - public Throwable getException() { - return exception; - } - - public ManagedClientConnection getConnection() { - return connection; - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java deleted file mode 100644 index e01ee4849..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn.tsccm; - -import static org.junit.Assert.*; - -import java.io.IOException; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -import org.apache.http.HttpHost; -import org.apache.http.conn.ClientConnectionOperator; -import org.apache.http.conn.ConnectionPoolTimeoutException; -import org.apache.http.conn.OperatedClientConnection; -import org.apache.http.conn.params.ConnPerRouteBean; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.impl.conn.DefaultClientConnectionOperator; -import org.apache.http.impl.conn.SchemeRegistryFactory; -import org.apache.http.localserver.LocalServerTestBase; -import org.apache.http.params.BasicHttpParams; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; - -@SuppressWarnings("deprecation") -@RunWith(MockitoJUnitRunner.class) -@Deprecated -public class TestConnPoolByRoute extends LocalServerTestBase { - - private ConnPoolByRoute impl; - private HttpRoute route = new HttpRoute(new HttpHost("localhost")); - private HttpRoute route2 = new HttpRoute(new HttpHost("localhost:8080")); - - @Mock private OperatedClientConnection mockConnection; - @Mock private OperatedClientConnection mockConnection2; - @Mock private ClientConnectionOperator mockOperator; - - @Before - public void setUp() throws Exception { - startServer(); - impl = new ConnPoolByRoute( - new DefaultClientConnectionOperator(SchemeRegistryFactory.createDefault()), - new ConnPerRouteBean(), 1, -1, TimeUnit.MILLISECONDS); - } - - private void useMockOperator() { - Mockito.reset(mockOperator); - impl = new ConnPoolByRoute( - mockOperator, new ConnPerRouteBean(), 1, -1, TimeUnit.MILLISECONDS); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection); - } - - @Test - public void testStatelessConnections() throws Exception { - final HttpHost target = getServerHttp(); - final HttpRoute route = new HttpRoute(target, null, false); - - ClientConnectionOperator operator = new DefaultClientConnectionOperator( - SchemeRegistryFactory.createDefault()); - - ConnPerRouteBean connPerRoute = new ConnPerRouteBean(3); - ConnPoolByRoute connPool = new ConnPoolByRoute(operator, connPerRoute, 20); - try { - // Allocate max possible entries - PoolEntryRequest r1 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e1 = r1.getPoolEntry(10, TimeUnit.SECONDS); - Assert.assertNotNull(e1); - - PoolEntryRequest r2 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e2 = r2.getPoolEntry(10, TimeUnit.SECONDS); - Assert.assertNotNull(e2); - - PoolEntryRequest r3 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e3 = r3.getPoolEntry(10, TimeUnit.SECONDS); - Assert.assertNotNull(e3); - - // Attempt to allocate one more. Expected to fail - PoolEntryRequest r4 = connPool.requestPoolEntry(route, null); - try { - r4.getPoolEntry(250, TimeUnit.MICROSECONDS); - Assert.fail("ConnectionPoolTimeoutException should have been thrown"); - } catch (ConnectionPoolTimeoutException expected) { - } - - // Free one - connPool.freeEntry(e3, true, -1, null); - - // This time the request should succeed - PoolEntryRequest r5 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e5 = r5.getPoolEntry(10, TimeUnit.SECONDS); - Assert.assertNotNull(e5); - - } finally { - connPool.shutdown(); - } - } - - @Test - public void testStatefullConnections() throws Exception { - final HttpHost target = getServerHttp(); - final HttpRoute route = new HttpRoute(target, null, false); - - ClientConnectionOperator operator = new DefaultClientConnectionOperator( - SchemeRegistryFactory.createDefault()); - - ConnPerRouteBean connPerRoute = new ConnPerRouteBean(3); - ConnPoolByRoute connPool = new ConnPoolByRoute(operator, connPerRoute, 20); - try { - // Allocate max possible entries - PoolEntryRequest r1 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e1 = r1.getPoolEntry(10, TimeUnit.SECONDS); - - PoolEntryRequest r2 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e2 = r2.getPoolEntry(10, TimeUnit.SECONDS); - - PoolEntryRequest r3 = connPool.requestPoolEntry(route, null); - BasicPoolEntry e3 = r3.getPoolEntry(10, TimeUnit.SECONDS); - - // Set states - e1.setState(Integer.valueOf(1)); - e2.setState(Integer.valueOf(2)); - e3.setState(Integer.valueOf(3)); - - // Release entries - connPool.freeEntry(e1, true, -1, null); - connPool.freeEntry(e2, true, -1, null); - connPool.freeEntry(e3, true, -1, null); - - // Request statefull entries - PoolEntryRequest r4 = connPool.requestPoolEntry(route, Integer.valueOf(2)); - BasicPoolEntry e4 = r4.getPoolEntry(10, TimeUnit.SECONDS); - - PoolEntryRequest r5 = connPool.requestPoolEntry(route, Integer.valueOf(3)); - BasicPoolEntry e5 = r5.getPoolEntry(10, TimeUnit.SECONDS); - - PoolEntryRequest r6 = connPool.requestPoolEntry(route, Integer.valueOf(1)); - BasicPoolEntry e6 = r6.getPoolEntry(10, TimeUnit.SECONDS); - - Assert.assertNotNull(e4.getState()); - Assert.assertNotNull(e5.getState()); - Assert.assertNotNull(e6.getState()); - - // Check whether we got the same objects - Assert.assertTrue(e4 == e2); - Assert.assertTrue(e5 == e3); - Assert.assertTrue(e6 == e1); - - // Release entries again - connPool.freeEntry(e4, true, -1, null); - connPool.freeEntry(e5, true, -1, null); - connPool.freeEntry(e6, true, -1, null); - - // Request an entry with a state not avaialable in the pool - PoolEntryRequest r7 = connPool.requestPoolEntry(route, Integer.valueOf(4)); - BasicPoolEntry e7 = r7.getPoolEntry(10, TimeUnit.SECONDS); - - // Make sure we got a closed connection and a stateless entry back - Assert.assertFalse(e7.getConnection().isOpen()); - Assert.assertNull(e7.getState()); - - } finally { - connPool.shutdown(); - } - } - - @Test(expected=IllegalArgumentException.class) - public void nullOperatorIsNotAllowed() { - new ConnPoolByRoute(null, new ConnPerRouteBean(), 1, -1, TimeUnit.MILLISECONDS); - } - - @Test(expected=IllegalArgumentException.class) - public void nullConnPerRouteIsNotAllowed() { - new ConnPoolByRoute(new DefaultClientConnectionOperator( - SchemeRegistryFactory.createDefault()), - null, 1, -1, TimeUnit.MILLISECONDS); - } - - @Test - public void deprecatedConstructorIsStillSupported() { - new ConnPoolByRoute(new DefaultClientConnectionOperator( - SchemeRegistryFactory.createDefault()), - new BasicHttpParams()); - } - - @Test - public void emptyPoolHasNoConnections() { - assertEquals(0, impl.getConnectionsInPool()); - } - - @Test - public void poolHasOneConnectionAfterRequestingOne() throws Exception { - useMockOperator(); - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - assertEquals(1, impl.getConnectionsInPool()); - } - - @Test - public void emptyPoolHasNoRouteSpecificConnections() { - assertEquals(0, impl.getConnectionsInPool(route)); - } - - @Test - public void routeSpecificPoolHasOneConnectionAfterRequestingOne() throws Exception { - useMockOperator(); - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - assertEquals(1, impl.getConnectionsInPool(route)); - } - - @Test - public void abortingPoolEntryRequestEarlyDoesNotCreateConnection() { - PoolEntryRequest req = impl.requestPoolEntry(route, new Object()); - req.abortRequest(); - assertEquals(0, impl.getConnectionsInPool(route)); - } - - @Test(expected=IllegalStateException.class) - public void cannotAcquireConnectionIfPoolShutdown() throws Exception { - impl.shutdown(); - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - } - - @Test - public void multipleShutdownsAreOk() { - impl.shutdown(); - impl.shutdown(); - } - - @Test - public void canAcquirePoolEntry() throws Exception { - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - } - - @Test - public void canRetrieveMaxTotalConnections() { - int max = (new Random()).nextInt(10) + 2; - impl.setMaxTotalConnections(max); - assertEquals(max, impl.getMaxTotalConnections()); - } - - @Test - public void closesFreedConnectionsWhenShutdown() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.shutdown(); - impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); - Mockito.verify(mockConnection, Mockito.atLeastOnce()).close(); - } - - @Test - public void deleteClosedConnectionsReclaimsPoolSpace() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); - assertFalse(impl.freeConnections.isEmpty()); - Mockito.when(mockConnection.isOpen()).thenReturn(false); - impl.deleteClosedConnections(); - assertTrue(impl.freeConnections.isEmpty()); - assertEquals(0, impl.numConnections); - } - - @Test - public void deleteClosedConnectionsDoesNotReclaimOpenConnections() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); - assertFalse(impl.freeConnections.isEmpty()); - Mockito.when(mockConnection.isOpen()).thenReturn(true); - impl.deleteClosedConnections(); - assertFalse(impl.freeConnections.isEmpty()); - assertEquals(1, impl.numConnections); - } - - @Test - public void closeIdleConnectionsClosesThoseThatHaveTimedOut() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); - Thread.sleep(200L); - impl.closeIdleConnections(1, TimeUnit.MILLISECONDS); - Mockito.verify(mockConnection, Mockito.atLeastOnce()).close(); - } - - @Test - public void closeIdleConnectionsDoesNotCloseThoseThatHaveNotTimedOut() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS); - impl.closeIdleConnections(3, TimeUnit.SECONDS); - Mockito.verify(mockConnection, Mockito.never()).close(); - } - - @Test - public void closeExpiredConnectionsClosesExpiredOnes() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, 1, TimeUnit.MILLISECONDS); - Thread.sleep(200L); - impl.closeExpiredConnections(); - Mockito.verify(mockConnection, Mockito.atLeastOnce()).close(); - } - - @Test - public void closeExpiredConnectionsDoesNotCloseUnexpiredOnes() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, 10, TimeUnit.SECONDS); - Thread.sleep(200L); - impl.closeExpiredConnections(); - Mockito.verify(mockConnection, Mockito.never()).close(); - } - - @Test - public void closesNonReusableConnections() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, false, 0, TimeUnit.MILLISECONDS); - Mockito.verify(mockConnection, Mockito.atLeastOnce()).close(); - } - - @Test - public void handlesExceptionsWhenClosingConnections() throws Exception { - useMockOperator(); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - Mockito.doThrow(new IOException()).when(mockConnection).close(); - impl.freeEntry(entry, false, 0, TimeUnit.MILLISECONDS); - } - - @Test - public void wakesUpWaitingThreadsWhenEntryAvailable() throws Exception { - useMockOperator(); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection); - impl.setMaxTotalConnections(1); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - final Flag f = new Flag(false); - Thread t = new Thread(new Runnable() { - public void run() { - try { - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - f.flag = true; - } catch (ConnectionPoolTimeoutException e) { - } catch (InterruptedException e) { - } - } - }); - t.start(); - Thread.sleep(100); - impl.freeEntry(entry, true, 1000, TimeUnit.MILLISECONDS); - Thread.sleep(100); - assertTrue(f.flag); - } - - @Test - public void wakesUpWaitingThreadsOnOtherRoutesWhenEntryAvailable() throws Exception { - useMockOperator(); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection); - impl.setMaxTotalConnections(1); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - final Flag f = new Flag(false); - Thread t = new Thread(new Runnable() { - public void run() { - try { - impl.requestPoolEntry(route2, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - f.flag = true; - } catch (ConnectionPoolTimeoutException e) { - } catch (InterruptedException e) { - } - } - }); - t.start(); - Thread.sleep(100); - impl.freeEntry(entry, true, 1000, TimeUnit.MILLISECONDS); - Thread.sleep(100); - assertTrue(f.flag); - } - - @Test - public void doesNotRecycleExpiredConnections() throws Exception { - useMockOperator(); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection, mockConnection2); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, 1, TimeUnit.MILLISECONDS); - Thread.sleep(200L); - BasicPoolEntry entry2 = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - assertNotSame(mockConnection, entry2.getConnection()); - } - - @Test - public void closesExpiredConnectionsWhenNotReusingThem() throws Exception { - useMockOperator(); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection, mockConnection2); - BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - impl.freeEntry(entry, true, 1, TimeUnit.MILLISECONDS); - Thread.sleep(200L); - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - Mockito.verify(mockConnection, Mockito.atLeastOnce()).close(); - } - - - @Test - public void wakesUpWaitingThreadsOnShutdown() throws Exception { - useMockOperator(); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection); - Mockito.when(mockOperator.createConnection()).thenReturn(mockConnection); - impl.setMaxTotalConnections(1); - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - final Flag f = new Flag(false); - Thread t = new Thread(new Runnable() { - public void run() { - try { - impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS); - } catch (IllegalStateException expected) { - f.flag = true; - } catch (ConnectionPoolTimeoutException e) { - } catch (InterruptedException e) { - } - } - }); - t.start(); - Thread.sleep(1); - impl.shutdown(); - Thread.sleep(1); - assertTrue(f.flag); - } - - private static class Flag { - public boolean flag; - public Flag(boolean init) { flag = init; } - } -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java deleted file mode 100644 index 86c08deac..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn.tsccm; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; - -import org.apache.http.HttpHost; -import org.apache.http.conn.ClientConnectionOperator; -import org.apache.http.conn.ClientConnectionRequest; -import org.apache.http.conn.ManagedClientConnection; -import org.apache.http.conn.params.ConnPerRoute; -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.scheme.SchemeSocketFactory; -import org.junit.Assert; -import org.junit.Test; - -/** - * Tests for spurious wakeups in WaitingThread. - * Requires some wrapping code to get at the lock and condition, - * which is required to trigger a wakeup without actually - * satisfying the condition. - * - */ -@Deprecated -public class TestSpuriousWakeup { - - public final static - HttpHost TARGET = new HttpHost("target.test.invalid"); - public final static - HttpRoute ROUTE = new HttpRoute(TARGET); - - /** - * An extended connection pool that gives access to some internals. - */ - private static class XConnPoolByRoute extends ConnPoolByRoute { - - /** The last WaitingThread object created. */ - protected WaitingThread newestWT; - - - public XConnPoolByRoute( - final ClientConnectionOperator operator, - final ConnPerRoute connPerRoute, - int maxTotalConnections) { - super(operator, connPerRoute, maxTotalConnections); - } - - @Override - protected synchronized - WaitingThread newWaitingThread(Condition cond, - RouteSpecificPool rospl) { - WaitingThread wt = super.newWaitingThread(cond, rospl); - newestWT = wt; - return wt; - } - - } // class XConnPoolByRoute - - - /** - * An extended TSCCM that uses XConnPoolByRoute. - */ - private static class XTSCCM extends ThreadSafeClientConnManager { - - /** The extended connection pool. */ - protected XConnPoolByRoute extendedCPBR; - - - public XTSCCM(SchemeRegistry schreg) { - super(schreg); - } - - @Override - protected ConnPoolByRoute createConnectionPool(long connTTL, TimeUnit connTTLUnit) { - extendedCPBR = new XConnPoolByRoute(connOperator, connPerRoute, 20); - // no connection GC required - return extendedCPBR; - } - - } // class XTSCCM - - @Test - public void testSpuriousWakeup() throws Exception { - SchemeRegistry schreg = new SchemeRegistry(); - SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory(); - schreg.register(new Scheme("http", 80, sf)); - - XTSCCM mgr = new XTSCCM(schreg); - try { - mgr.setMaxTotal(1); - mgr.setDefaultMaxPerRoute(1); - - // take out the only connection - ClientConnectionRequest connRequest = mgr.requestConnection(ROUTE, null); - ManagedClientConnection conn = connRequest.getConnection(0, null); - Assert.assertNotNull(conn); - - // send a thread waiting - GetConnThread gct = new GetConnThread(mgr, ROUTE, 0L); - gct.start(); - Thread.sleep(100); // give extra thread time to block - - Assert.assertEquals("thread not waiting", - Thread.State.WAITING, gct.getState()); - - // get access to the objects we need - Lock lck = mgr.extendedCPBR.getLock(); - Condition cnd = mgr.extendedCPBR.newestWT.getCondition(); - - // Now trigger spurious wakeups. We'll do it several times - // in a loop, just to be sure the connection manager has a - // fair chance of misbehaving, and the gct to register it. - - for (int i=0; i<3; i++) { - if (i > 0) - Thread.sleep(333); // don't go too fast - - try { - lck.lock(); - cnd.signalAll(); // this is the spurious wakeup - } finally { - lck.unlock(); - } - - // now give the waiting thread some time to register a wakeup - Thread.sleep(100); - - Assert.assertEquals("thread no longer waiting, iteration " + i, - Thread.State.WAITING, gct.getState()); - } - } finally { - // don't worry about releasing the connection, just shut down - mgr.shutdown(); - } - } - -} diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java deleted file mode 100644 index 8d031722a..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * ==================================================================== - * 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 - * . - * - */ - -package org.apache.http.impl.conn.tsccm; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -import org.apache.http.HttpHost; -import org.apache.http.conn.params.ConnPerRoute; -import org.apache.http.conn.params.ConnPerRouteBean; -import org.apache.http.conn.routing.HttpRoute; -import org.junit.Assert; -import org.junit.Test; - -/** - * Tests for WaitingThread. - */ -@Deprecated -public class TestWaitingThread { - - public final static - HttpHost TARGET = new HttpHost("target.test.invalid"); - - @Test - public void testConstructor() { - try { - new WaitingThread(null, null); - Assert.fail("null condition not detected"); - } catch (IllegalArgumentException iax) { - // expected - } - - Lock lck = new ReentrantLock(); - Condition cnd = lck.newCondition(); - - WaitingThread wt = new WaitingThread(cnd, null); - Assert.assertEquals("wrong condition", cnd, wt.getCondition()); - Assert.assertNull ("pool from nowhere", wt.getPool()); - Assert.assertNull ("thread from nowhere", wt.getThread()); - - HttpRoute route = new HttpRoute(TARGET); - ConnPerRoute connPerRoute = new ConnPerRouteBean(10); - RouteSpecificPool rospl = new RouteSpecificPool(route, connPerRoute); - wt = new WaitingThread(cnd, rospl); - Assert.assertEquals("wrong condition", cnd, wt.getCondition()); - Assert.assertEquals("wrong pool", rospl, wt.getPool()); - Assert.assertNull ("thread from nowhere", wt.getThread()); - } - - @Test - public void testAwaitWakeup() throws InterruptedException { - - Lock lck = new ReentrantLock(); - Condition cnd = lck.newCondition(); - WaitingThread wt = new WaitingThread(cnd, null); - - AwaitThread ath = new AwaitThread(wt, lck, null); - ath.start(); - Thread.sleep(100); // give extra thread time to block - - Assert.assertNull("thread caught exception", ath.getException()); - Assert.assertTrue("thread not waiting", ath.isWaiting()); - Assert.assertEquals("wrong thread", ath, wt.getThread()); - - Thread.sleep(500); // just for fun, let it wait for some time - // this may fail due to a spurious wakeup - Assert.assertTrue("thread not waiting, spurious wakeup?", ath.isWaiting()); - - try { - lck.lock(); - wt.wakeup(); - } finally { - lck.unlock(); - } - ath.join(10000); - - Assert.assertFalse("thread still waiting", ath.isWaiting()); - Assert.assertNull("thread caught exception", ath.getException()); - Assert.assertNull("thread still there", wt.getThread()); - } - - @Test - public void testInterrupt() throws InterruptedException { - - Lock lck = new ReentrantLock(); - Condition cnd = lck.newCondition(); - WaitingThread wt = new WaitingThread(cnd, null); - - AwaitThread ath = new AwaitThread(wt, lck, null); - ath.start(); - Thread.sleep(100); // give extra thread time to block - - Assert.assertNull("thread caught exception", ath.getException()); - Assert.assertTrue("thread not waiting", ath.isWaiting()); - Assert.assertEquals("wrong thread", ath, wt.getThread()); - - ath.interrupt(); - Thread.sleep(100); // give extra thread time to wake up - - Assert.assertFalse("thread still waiting", ath.isWaiting()); - Assert.assertNotNull("thread didn't catch exception", ath.getException()); - Assert.assertTrue("thread caught wrong exception", - ath.getException() instanceof InterruptedException); - Assert.assertNull("thread still there", wt.getThread()); - } - - @Test - public void testIllegal() throws InterruptedException { - - Lock lck = new ReentrantLock(); - Condition cnd = lck.newCondition(); - WaitingThread wt = new WaitingThread(cnd, null); - - try { - lck.lock(); - wt.wakeup(); - Assert.fail("missing waiter not detected"); - } catch (IllegalStateException isx) { - // expected - } finally { - lck.unlock(); - } - - AwaitThread ath1 = new AwaitThread(wt, lck, null); - ath1.start(); - Thread.sleep(100); // give extra thread time to block - - Assert.assertNull("thread caught exception", ath1.getException()); - Assert.assertTrue("thread not waiting", ath1.isWaiting()); - Assert.assertEquals("wrong thread", ath1, wt.getThread()); - - AwaitThread ath2 = new AwaitThread(wt, lck, null); - ath2.start(); - Thread.sleep(100); // give extra thread time to try to block - - Assert.assertFalse("thread waiting", ath2.isWaiting()); - Assert.assertNotNull("thread didn't catch exception", ath2.getException()); - Assert.assertTrue("thread caught wrong exception", - ath2.getException() instanceof IllegalStateException); - - // clean up by letting the threads terminate - ath1.interrupt(); - ath2.interrupt(); - } - -}