From 458575bf59400ad534d70793d1bd9d9ab2e259a1 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Thu, 28 Mar 2013 13:30:06 +0000 Subject: [PATCH] HTTPCLIENT-1322: Factory method + example for HttpAsyncClientWithFuture. Follow-up to HTTPCLIENT-1307 Contributed by Jilles van Gurp git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1462087 13f79535-47bb-0310-9956-ffa450edef68 --- .../client/ClientAsyncWithFuture.java | 117 ++++++++++++++++++ .../async/HttpAsyncClientWithFuture.java | 18 ++- .../apache/http/impl/client/HttpClients.java | 18 +++ .../async/HttpClientWithFutureTest.java | 15 +-- 4 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 httpclient/src/examples/org/apache/http/examples/client/ClientAsyncWithFuture.java diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientAsyncWithFuture.java b/httpclient/src/examples/org/apache/http/examples/client/ClientAsyncWithFuture.java new file mode 100644 index 000000000..3dd47b82d --- /dev/null +++ b/httpclient/src/examples/org/apache/http/examples/client/ClientAsyncWithFuture.java @@ -0,0 +1,117 @@ +/* + * ==================================================================== + * 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.client; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.async.HttpAsyncClientFutureTask; +import org.apache.http.client.async.HttpAsyncClientWithFuture; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.concurrent.FutureCallback; +import org.apache.http.impl.client.HttpClients; + +public class ClientAsyncWithFuture { + + public static void main(String[] args) throws Exception { + // the simplest way to create a HttpAsyncClientWithFuture + HttpAsyncClientWithFuture client = HttpClients.createAsync(3); + + // Because things are asynchronous, you must provide a ResponseHandler + ResponseHandler handler = new ResponseHandler() { + public Boolean handleResponse(HttpResponse response) throws ClientProtocolException, IOException { + // simply return true if the status was OK + return response.getStatusLine().getStatusCode() == 200; + } + }; + + // Simple request ... + HttpGet request1 = new HttpGet("http://google.com"); + HttpAsyncClientFutureTask futureTask1 = client.execute(request1, handler); + Boolean wasItOk1 = futureTask1.get(); + System.out.println("It was ok? " + wasItOk1); + + // Cancel a request + try { + HttpGet request2 = new HttpGet("http://google.com"); + HttpAsyncClientFutureTask futureTask2 = client.execute(request2, handler); + futureTask2.cancel(true); + Boolean wasItOk2 = futureTask2.get(); + System.out.println("It was cancelled so it should never print this: " + wasItOk2); + } catch (CancellationException e) { + System.out.println("We cancelled it, so this is expected"); + } + + // Request with a timeout + HttpGet request3 = new HttpGet("http://google.com"); + HttpAsyncClientFutureTask futureTask3 = client.execute(request3, handler); + Boolean wasItOk3 = futureTask3.get(10, TimeUnit.SECONDS); + System.out.println("It was ok? " + wasItOk3); + + FutureCallback callback = new FutureCallback() { + public void completed(Boolean result) { + System.out.println("completed with " + result); + } + + public void failed(Exception ex) { + System.out.println("failed with " + ex.getMessage()); + } + + public void cancelled() { + System.out.println("cancelled"); + } + }; + + // Simple request with a callback + HttpGet request4 = new HttpGet("http://google.com"); + // using a null HttpContext here since it is optional + // the callback will be called when the task completes, fails, or is cancelled + HttpAsyncClientFutureTask futureTask4 = client.execute(request4, null, handler, callback); + Boolean wasItOk4 = futureTask4.get(10, TimeUnit.SECONDS); + System.out.println("It was ok? " + wasItOk4); + + // Multiple requests, with a callback + HttpGet request5 = new HttpGet("http://google.com"); + HttpGet request6 = new HttpGet("http://bing.com"); + HttpGet request7 = new HttpGet("http://yahoo.com"); + // using a null HttpContext here since it is optional + // the callback will be called for each request as their responses come back. + List> futureTask = client.executeMultiple(null, handler, callback,20, + TimeUnit.SECONDS, request5, request6, request7); + // you can still access the futures directly, if you want. The futures are in the same order as the requests. + for (Future future : futureTask) { + System.out.println("another result " + future.get()); + } + } + +} \ No newline at end of file diff --git a/httpclient/src/main/java/org/apache/http/client/async/HttpAsyncClientWithFuture.java b/httpclient/src/main/java/org/apache/http/client/async/HttpAsyncClientWithFuture.java index fa0b15574..52b3fd82e 100644 --- a/httpclient/src/main/java/org/apache/http/client/async/HttpAsyncClientWithFuture.java +++ b/httpclient/src/main/java/org/apache/http/client/async/HttpAsyncClientWithFuture.java @@ -26,6 +26,8 @@ */ package org.apache.http.client.async; +import java.io.Closeable; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -33,12 +35,14 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.http.annotation.ThreadSafe; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.concurrent.FutureCallback; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.protocol.HttpContext; /** @@ -47,13 +51,15 @@ * Similar to the non-blockcing HttpAsyncClient, a callback handler api is provided. */ @ThreadSafe -public class HttpAsyncClientWithFuture { +public class HttpAsyncClientWithFuture implements Closeable { final HttpClient httpclient; private final ExecutorService executorService; private final ConnectionMetrics metrics = new ConnectionMetrics(); + private final AtomicBoolean closed = new AtomicBoolean(false); + /** * Create a new HttpAsyncClientWithFuture. * @@ -113,6 +119,9 @@ public HttpAsyncClientFutureTask execute( final HttpContext context, final ResponseHandler responseHandler, final FutureCallback callback) throws InterruptedException { + if(closed.get()) { + throw new IllegalStateException("Close has been called on this httpclient instance."); + } metrics.scheduledConnections.incrementAndGet(); final HttpAsyncClientCallable callable = new HttpAsyncClientCallable( httpclient, request, context, responseHandler, callback, metrics); @@ -188,4 +197,11 @@ public ConnectionMetrics metrics() { return metrics; } + public void close() throws IOException { + closed.set(true); + executorService.shutdownNow(); + if(httpclient instanceof CloseableHttpClient) { + ((CloseableHttpClient) httpclient).close(); + } + } } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpClients.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpClients.java index c51bb194c..6b23f138c 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/HttpClients.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpClients.java @@ -27,7 +27,12 @@ package org.apache.http.impl.client; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + import org.apache.http.annotation.Immutable; +import org.apache.http.client.HttpClient; +import org.apache.http.client.async.HttpAsyncClientWithFuture; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -61,4 +66,17 @@ public static CloseableHttpClient createMinimal(final HttpClientConnectionManage return new MinimalHttpClient(connManager); } + /** + * Creates a simple HttpAsyncClientWithFuture with an executor with the specified number of threads and a matching httpclient. + * @param threads + * the number of connections and threads used for the httpclient and the executor used by @see + * HttpAsyncClientWithFuture. + * @return a HttpAsyncClientWithFuture with an httpclient and executor that can handle the specified amount of + * threads/connections. + */ + public static HttpAsyncClientWithFuture createAsync(int threads) { + HttpClient httpClient = HttpClientBuilder.create().setMaxConnPerRoute(5).setMaxConnTotal(5).build(); + ExecutorService executorService = Executors.newFixedThreadPool(5); + return new HttpAsyncClientWithFuture(httpClient, executorService); + } } diff --git a/httpclient/src/test/java/org/apache/http/client/async/HttpClientWithFutureTest.java b/httpclient/src/test/java/org/apache/http/client/async/HttpClientWithFutureTest.java index e3a411e5e..ea0f1023d 100644 --- a/httpclient/src/test/java/org/apache/http/client/async/HttpClientWithFutureTest.java +++ b/httpclient/src/test/java/org/apache/http/client/async/HttpClientWithFutureTest.java @@ -31,8 +31,6 @@ import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -47,8 +45,7 @@ import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.concurrent.FutureCallback; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; import org.apache.http.localserver.LocalTestServer; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestHandler; @@ -61,8 +58,6 @@ public class HttpClientWithFutureTest { private LocalTestServer localServer; private String uri; private HttpAsyncClientWithFuture httpAsyncClientWithFuture; - private CloseableHttpClient httpClient; - private ExecutorService executorService; private final AtomicBoolean blocked = new AtomicBoolean(false); @@ -87,18 +82,14 @@ public void handle( this.localServer.start(); final InetSocketAddress address = localServer.getServiceAddress(); uri = "http://" + address.getHostName() + ":" + address.getPort() + "/wait"; - - httpClient = HttpClientBuilder.create().setMaxConnPerRoute(5).setMaxConnTotal(5).build(); - executorService = Executors.newFixedThreadPool(5); - httpAsyncClientWithFuture = new HttpAsyncClientWithFuture(httpClient, executorService); + httpAsyncClientWithFuture = HttpClients.createAsync(5); } @After public void after() throws Exception { blocked.set(false); // any remaining requests should unblock this.localServer.stop(); - httpClient.close(); - executorService.shutdownNow(); + httpAsyncClientWithFuture.close(); } @Test