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,

Reply via email to