Repository: incubator-juneau Updated Branches: refs/heads/master 7b883daef -> 5b6db82c7
Add Future methods to RestCall class for async handling of HTTP calls. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/5b6db82c Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/5b6db82c Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/5b6db82c Branch: refs/heads/master Commit: 5b6db82c7c4ec08850e38ea9e34cddb2afd3bcac Parents: 7b883da Author: JamesBognar <[email protected]> Authored: Fri Apr 7 17:16:31 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Fri Apr 7 17:16:31 2017 -0400 ---------------------------------------------------------------------- juneau-core/src/main/javadoc/overview.html | 8 ++ .../org/apache/juneau/rest/client/RestCall.java | 97 +++++++++++++++++++- .../apache/juneau/rest/client/RestClient.java | 15 ++- .../juneau/rest/client/RestClientBuilder.java | 26 +++++- .../juneau/rest/test/ClientFuturesResource.java | 35 +++++++ .../java/org/apache/juneau/rest/test/Root.java | 1 + .../juneau/rest/test/ClientFuturesTest.java | 50 ++++++++++ .../org/apache/juneau/rest/test/_TestSuite.java | 1 + 8 files changed, 229 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-core/src/main/javadoc/overview.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html index afcbb9e..b6b2006 100644 --- a/juneau-core/src/main/javadoc/overview.html +++ b/juneau-core/src/main/javadoc/overview.html @@ -5906,6 +5906,14 @@ <li>{@link org.apache.juneau.rest.client.RestCall#userInfo(String)} <li>{@link org.apache.juneau.rest.client.RestCall#scheme(String)} </ul> + <li>New methods added to allow for asynchronous HTTP calls: + <ul> + <li>{@link org.apache.juneau.rest.client.RestClientBuilder#executorService(ExecutorService,boolean)} + <li>{@link org.apache.juneau.rest.client.RestCall#runFuture()} + <li>{@link org.apache.juneau.rest.client.RestCall#getResponseFuture(Class)} + <li>{@link org.apache.juneau.rest.client.RestCall#getResponseFuture(Type,Type...)} + <li>{@link org.apache.juneau.rest.client.RestCall#getResponseAsStringFuture()} + </ul> </ul> <h6 class='topic'>org.apache.juneau.microservice</h6> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java ---------------------------------------------------------------------- diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java index e1aec11..3e36cf3 100644 --- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java +++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java @@ -16,6 +16,7 @@ import java.io.*; import java.lang.reflect.*; import java.net.*; import java.util.*; +import java.util.concurrent.*; import java.util.logging.*; import java.util.regex.*; @@ -80,6 +81,7 @@ public final class RestCall { private Serializer serializer; private Parser parser; private URIBuilder uriBuilder; + private final ExecutorService executorService; /** * Constructs a REST call with the specified method name. @@ -99,6 +101,7 @@ public final class RestCall { this.retryInterval = client.retryInterval; this.serializer = client.serializer; this.parser = client.parser; + this.executorService = client.executorService; uriBuilder = new URIBuilder(uri); } @@ -1017,7 +1020,7 @@ public final class RestCall { * } * </p> * - * @return This object (for method chaining). + * @return The HTTP status code. * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. */ public int run() throws RestCallException { @@ -1042,6 +1045,25 @@ public final class RestCall { } /** + * Same as {@link #run()} but allows you to run the call asynchronously. + * <p> + * This method cannot be used unless the executor service was defined on the client using {@link RestClientBuilder#executorService(ExecutorService,boolean)}. + * + * @return The HTTP status code. + * @throws RestCallException If the executor service was not defined. + */ + public Future<Integer> runFuture() throws RestCallException { + return getExecutorService().submit( + new Callable<Integer>() { + @Override /* Callable */ + public Integer call() throws Exception { + return run(); + } + } + ); + } + + /** * Connects to the REST resource. * <p> * If this is a <code>PUT</code> or <code>POST</code>, also sends the input to the remote resource.<br> @@ -1303,6 +1325,25 @@ public final class RestCall { } /** + * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously. + * <p> + * This method cannot be used unless the executor service was defined on the client using {@link RestClientBuilder#executorService(ExecutorService,boolean)}. + * + * @return The response as a string. + * @throws RestCallException If the executor service was not defined. + */ + public Future<String> getResponseAsStringFuture() throws RestCallException { + return getExecutorService().submit( + new Callable<String>() { + @Override /* Callable */ + public String call() throws Exception { + return getResponseAsString(); + } + } + ); + } + + /** * Same as {@link #getResponse(Type, Type...)} except optimized for a non-parameterized class. * <p> * This is the preferred parse method for simple types since you don't need to cast the results. @@ -1339,6 +1380,28 @@ public final class RestCall { } /** + * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously. + * <p> + * This method cannot be used unless the executor service was defined on the client using {@link RestClientBuilder#executorService(ExecutorService,boolean)}. + * + * @param <T> The class type of the object being created. + * See {@link #getResponse(Type, Type...)} for details. + * @param type The object type to create. + * @return The parsed object. + * @throws RestCallException If the executor service was not defined. + */ + public <T> Future<T> getResponseFuture(final Class<T> type) throws RestCallException { + return getExecutorService().submit( + new Callable<T>() { + @Override /* Callable */ + public T call() throws Exception { + return getResponse(type); + } + } + ); + } + + /** * Parses HTTP body into the specified object type. * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). * @@ -1391,6 +1454,32 @@ public final class RestCall { } /** + * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously. + * <p> + * This method cannot be used unless the executor service was defined on the client using {@link RestClientBuilder#executorService(ExecutorService,boolean)}. + * + * @param <T> The class type of the object being created. + * See {@link #getResponse(Type, Type...)} for details. + * @param type The object type to create. + * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} + * @param args The type arguments of the class if it's a collection or map. + * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} + * <br>Ignored if the main type is not a map or collection. + * @return The parsed object. + * @throws RestCallException If the executor service was not defined. + */ + public <T> Future<T> getResponseFuture(final Type type, final Type...args) throws RestCallException { + return getExecutorService().submit( + new Callable<T>() { + @Override /* Callable */ + public T call() throws Exception { + return getResponse(type, args); + } + } + ); + } + + /** * Parses the output from the connection into the specified type and then wraps that in a {@link PojoRest}. * <p> * Useful if you want to quickly retrieve a single value from inside of a larger JSON document. @@ -1502,6 +1591,12 @@ public final class RestCall { return this; } + private ExecutorService getExecutorService() throws RestCallException { + if (executorService == null) + throw new RestCallException("Future method cannot be called. Executor service was not defined via the RestClientBuilder.executorService() method."); + return executorService; + } + /** * Adds a {@link RestCallLogger} to the list of interceptors on this class. * http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java ---------------------------------------------------------------------- diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java index bbdb98e..12b578f 100644 --- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java +++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java @@ -67,6 +67,8 @@ public class RestClient extends CoreObject { final long retryInterval; final boolean debug; final RestCallInterceptor[] interceptors; + final ExecutorService executorService; + final boolean executorServiceShutdownOnClose; /** * Create a new REST client. @@ -85,6 +87,8 @@ public class RestClient extends CoreObject { * @param retries * @param retryInterval * @param debug + * @param executorService + * @param executorServiceShutdownOnClose */ public RestClient( PropertyStore propertyStore, @@ -101,7 +105,9 @@ public class RestClient extends CoreObject { RetryOn retryOn, int retries, long retryInterval, - boolean debug) { + boolean debug, + ExecutorService executorService, + boolean executorServiceShutdownOnClose) { super(propertyStore); this.httpClient = httpClient; this.keepHttpClientOpen = keepHttpClientOpen; @@ -129,6 +135,9 @@ public class RestClient extends CoreObject { creationStack = Thread.currentThread().getStackTrace(); else creationStack = null; + + this.executorService = executorService; + this.executorServiceShutdownOnClose = executorServiceShutdownOnClose; } /** @@ -141,6 +150,8 @@ public class RestClient extends CoreObject { isClosed = true; if (httpClient != null && ! keepHttpClientOpen) httpClient.close(); + if (executorService != null && executorServiceShutdownOnClose) + executorService.shutdown(); if (Boolean.getBoolean("org.apache.juneau.rest.client.RestClient.trackLifecycle")) closedStack = Thread.currentThread().getStackTrace(); } @@ -153,6 +164,8 @@ public class RestClient extends CoreObject { try { if (httpClient != null && ! keepHttpClientOpen) httpClient.close(); + if (executorService != null && executorServiceShutdownOnClose) + executorService.shutdown(); } catch (Throwable t) {} if (Boolean.getBoolean("org.apache.juneau.rest.client.RestClient.trackLifecycle")) closedStack = Thread.currentThread().getStackTrace(); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java index 869b922..70f6970 100644 --- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java +++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java @@ -76,7 +76,8 @@ public class RestClientBuilder extends CoreObjectBuilder { private int retries = 1; private long retryInterval = -1; private RetryOn retryOn = RetryOn.DEFAULT; - private boolean debug; + private boolean debug, executorServiceShutdownOnClose; + private ExecutorService executorService; /** * Constructor, default settings. @@ -140,7 +141,7 @@ public class RestClientBuilder extends CoreObjectBuilder { UrlEncodingSerializer us = new SerializerBuilder(propertyStore).build(UrlEncodingSerializer.class); - return new RestClient(propertyStore, httpClient, keepHttpClientOpen, s, p, us, headers, interceptors, remoteableServletUri, remoteableServiceUriMap, rootUrl, retryOn, retries, retryInterval, debug); + return new RestClient(propertyStore, httpClient, keepHttpClientOpen, s, p, us, headers, interceptors, remoteableServletUri, remoteableServiceUriMap, rootUrl, retryOn, retries, retryInterval, debug, executorService, executorServiceShutdownOnClose); } catch (Exception e) { throw new RuntimeException(e); } @@ -425,6 +426,27 @@ public class RestClientBuilder extends CoreObjectBuilder { return this; } + /** + * Defines the executor service to use when calling future methods on the {@link RestCall} class. + * <p> + * You must specify the executor service if you want to use any of the following methods: + * <ul> + * <li>{@link RestCall#runFuture()} + * <li>{@link RestCall#getResponseFuture(Class)} + * <li>{@link RestCall#getResponseFuture(Type,Type...)} + * <li>{@link RestCall#getResponseAsString()} + * </ul> + * + * @param executorService The executor service. + * @param shutdownOnClose Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called. + * @return This object (for method chaining). + */ + public RestClientBuilder executorService(ExecutorService executorService, boolean shutdownOnClose) { + this.executorService = executorService; + this.executorServiceShutdownOnClose = shutdownOnClose; + return this; + } + //-------------------------------------------------------------------------------- // HTTP headers http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ClientFuturesResource.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ClientFuturesResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ClientFuturesResource.java new file mode 100644 index 0000000..0a4202a --- /dev/null +++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ClientFuturesResource.java @@ -0,0 +1,35 @@ +// *************************************************************************************************************************** +// * 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. * +// *************************************************************************************************************************** +package org.apache.juneau.rest.test; + +import org.apache.juneau.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.rest.annotation.*; + +/** + * JUnit automated testcase resource. + */ +@RestResource( + path="/testClientFutures" +) +public class ClientFuturesResource extends RestServletDefault { + private static final long serialVersionUID = 1L; + + //==================================================================================================== + // Test GET + //==================================================================================================== + @RestMethod(name="GET", path="/") + public ObjectMap test1(RestRequest req) throws Exception { + return new ObjectMap().append("foo","bar"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java index 10bbb18..fabb545 100644 --- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java +++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java @@ -24,6 +24,7 @@ import org.apache.juneau.rest.labels.*; BeanContextPropertiesResource.class, CallbackStringsResource.class, CharsetEncodingsResource.class, + ClientFuturesResource.class, ClientVersionResource.class, ConfigResource.class, ContentResource.class, http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ClientFuturesTest.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ClientFuturesTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ClientFuturesTest.java new file mode 100644 index 0000000..b361ba5 --- /dev/null +++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ClientFuturesTest.java @@ -0,0 +1,50 @@ +// *************************************************************************************************************************** +// * 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. * +// *************************************************************************************************************************** +package org.apache.juneau.rest.test; + +import static org.apache.juneau.rest.test.TestUtils.*; +import static org.junit.Assert.*; + +import java.util.concurrent.*; + +import org.apache.juneau.*; +import org.apache.juneau.rest.client.*; +import org.junit.*; + +public class ClientFuturesTest extends RestTestcase { + + private static String URL = "/testClientFutures"; + + //==================================================================================================== + // Basic tests + //==================================================================================================== + @Test + public void test() throws Exception { + RestClient client = null; + try { + ExecutorService es = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1)); + client = TestMicroservice.client().executorService(es, true).build(); + + Future<Integer> f = client.doGet(URL).runFuture(); + assertEquals(200, f.get().intValue()); + + Future<ObjectMap> f2 = client.doGet(URL).getResponseFuture(ObjectMap.class); + assertObjectEquals("{foo:'bar'}", f2.get()); + + Future<String> f3 = client.doGet(URL).getResponseAsStringFuture(); + assertObjectEquals("'{\"foo\":\"bar\"}'", f3.get()); + } finally { + client.closeQuietly(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/5b6db82c/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java index 0714aca..ec817ff 100644 --- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java +++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java @@ -28,6 +28,7 @@ import org.junit.runners.Suite.*; BeanContextPropertiesTest.class, CallbackStringsTest.class, CharsetEncodingsTest.class, + ClientFuturesTest.class, ClientVersionTest.class, ConfigTest.class, ContentTest.class,
