You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/04/07 21:16:35 UTC

incubator-juneau git commit: Add Future methods to RestCall class for async handling of HTTP calls.

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 <ja...@apache.org>
Authored: Fri Apr 7 17:16:31 2017 -0400
Committer: JamesBognar <ja...@apache.org>
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,