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 2020/03/19 13:54:15 UTC

[juneau] branch master updated: JUNEAU-183

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 7f09fc1  JUNEAU-183
7f09fc1 is described below

commit 7f09fc15a10979d68e9430ea30f1fc4cc4933689
Author: JamesBognar <ja...@apache.org>
AuthorDate: Thu Mar 19 09:53:59 2020 -0400

    JUNEAU-183
    
    Remote methods should support Future return types.
---
 juneau-doc/docs/ReleaseNotes/8.1.4.html            |  2 +
 .../01.RestProxies/02.RemoteMethod.html            |  5 +-
 .../rest/client2/RemoteMethodAnnotationTest.java   | 90 +++++++++++++++++++++-
 .../rest/client/remote/RemoteMethodReturn.java     | 21 ++++-
 .../org/apache/juneau/rest/client2/RestClient.java | 55 ++++++++-----
 5 files changed, 148 insertions(+), 25 deletions(-)

diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index 0d7cfd8..2c9b6db 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -332,6 +332,8 @@
 			<li>Better integration with HttpClient.
 			<li>New fluent-style methods with many new convenience methods.
 		</ul>
+	<li>
+		<ja>@RemoteMethod</ja>-annotated methods can now return {@link java.util.concurrent.Future Futures} for concurrent processing of requests.
 </ul>
 
 <h5 class='topic w800'>juneau-doc</h5>
diff --git a/juneau-doc/docs/Topics/09.juneau-rest-client/01.RestProxies/02.RemoteMethod.html b/juneau-doc/docs/Topics/09.juneau-rest-client/01.RestProxies/02.RemoteMethod.html
index 4d62e89..1d97872 100644
--- a/juneau-doc/docs/Topics/09.juneau-rest-client/01.RestProxies/02.RemoteMethod.html
+++ b/juneau-doc/docs/Topics/09.juneau-rest-client/01.RestProxies/02.RemoteMethod.html
@@ -13,6 +13,7 @@
  ***************************************************************************************************************************/
  -->
 
+{8.1.4-updated}
 @RemoteMethod
 
 <p>
@@ -125,7 +126,7 @@
 </p>
 <ul class='spaced-list'>
 	<li>
-		<jk>void</jk> 
+		<jk>void</jk>/{@link java.lang.Void}
 		- Don't parse any response.  
 		<br>Note that the method will still throw a runtime exception if an error HTTP status is returned.
 	<li>
@@ -143,6 +144,8 @@
 	<li>
 		{@link java.io.InputStream} 
 		- Returns access to the raw input stream of the response.
+	<li>
+		A {@link java.util.concurrent.Future} of anything on this list. 
 </ul>
 
 <p>
diff --git a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RemoteMethodAnnotationTest.java b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RemoteMethodAnnotationTest.java
index 8b590c5..1714af9 100644
--- a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RemoteMethodAnnotationTest.java
+++ b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RemoteMethodAnnotationTest.java
@@ -15,6 +15,7 @@ package org.apache.juneau.rest.client2;
 import static org.junit.Assert.*;
 
 import java.io.*;
+import java.util.concurrent.*;
 
 import org.apache.http.*;
 import org.apache.juneau.http.annotation.Body;
@@ -71,6 +72,15 @@ public class RemoteMethodAnnotationTest {
 		public String postA02();
 	}
 
+	@Remote
+	public static interface A02 {
+		public Future<String> doGet();
+		public Future<String> doGET();
+		public Future<String> doFoo();
+		public Future<String> getA01();
+		public Future<String> postA02();
+	}
+
 	@Test
 	public void a01_inferredMethodsAndPaths() throws Exception {
 		A01 t = MockRemote.build(A01.class, A.class, null);
@@ -81,6 +91,16 @@ public class RemoteMethodAnnotationTest {
 		assertEquals("baz", t.postA02());
 	}
 
+	@Test
+	public void a02_inferredMethodsAndPaths_futures() throws Exception {
+		A02 t = MockRemote.build(A02.class, A.class, null);
+		assertEquals("foo", t.doGet().get());
+		assertEquals("foo", t.doGET().get());
+		assertEquals("qux", t.doFoo().get());
+		assertEquals("bar", t.getA01().get());
+		assertEquals("baz", t.postA02().get());
+	}
+
 	//=================================================================================================================
 	// Return types
 	//=================================================================================================================
@@ -113,13 +133,21 @@ public class RemoteMethodAnnotationTest {
 	@Remote
 	public static interface B01 {
 		public void b01();
-
 		public String b02();
 		public HttpResponse b02a();
 		public Reader b02b();
 		public InputStream b02c();
 	}
 
+	@Remote
+	public static interface B02 {
+		public Future<Void> b01();
+		public Future<String> b02();
+		public Future<HttpResponse> b02a();
+		public Future<Reader> b02b();
+		public Future<InputStream> b02c();
+	}
+
 	@Test
 	public void b01_returnTypes() throws Exception {
 		B01 t = MockRemote.build(B01.class, B.class, null);
@@ -130,6 +158,16 @@ public class RemoteMethodAnnotationTest {
 		assertEquals("qux", IOUtils.read(t.b02c()));
 	}
 
+	@Test
+	public void b02_returnTypes_futures() throws Exception {
+		B02 t = MockRemote.build(B02.class, B.class, null);
+		t.b01().get();
+		assertEquals("foo", t.b02().get());
+		assertEquals("bar", IOUtils.read(t.b02a().get().getEntity().getContent()));
+		assertEquals("baz", IOUtils.read(t.b02b().get()));
+		assertEquals("qux", IOUtils.read(t.b02c().get()));
+	}
+
 	//=================================================================================================================
 	// Return types, JSON
 	//=================================================================================================================
@@ -159,6 +197,22 @@ public class RemoteMethodAnnotationTest {
 		public InputStream c01d(@Body String foo);
 	}
 
+	@Remote
+	public static interface C02 {
+
+		@RemoteMethod(method="POST",path="c01")
+		public Future<String> c01a(@Body String foo);
+
+		@RemoteMethod(method="POST",path="c01")
+		public Future<HttpResponse> c01b(@Body String foo);
+
+		@RemoteMethod(method="POST",path="c01")
+		public Future<Reader> c01c(@Body String foo);
+
+		@RemoteMethod(method="POST",path="c01")
+		public Future<InputStream> c01d(@Body String foo);
+	}
+
 	@Test
 	public void c01_returnTypes_json() throws Exception {
 		C01 t = MockRemote.build(C01.class, C.class, Json.DEFAULT);
@@ -168,6 +222,15 @@ public class RemoteMethodAnnotationTest {
 		assertEquals("'foo'", IOUtils.read(t.c01d("foo")));
 	}
 
+	@Test
+	public void c02_returnTypes_json_futures() throws Exception {
+		C02 t = MockRemote.build(C02.class, C.class, Json.DEFAULT);
+		assertEquals("foo", t.c01a("foo").get());
+		assertEquals("'foo'", IOUtils.read(t.c01b("foo").get().getEntity().getContent()));
+		assertEquals("'foo'", IOUtils.read(t.c01c("foo").get()));
+		assertEquals("'foo'", IOUtils.read(t.c01d("foo").get()));
+	}
+
 	//=================================================================================================================
 	// Return types, part serialization
 	//=================================================================================================================
@@ -198,6 +261,22 @@ public class RemoteMethodAnnotationTest {
 		public InputStream d01d(@Body String foo);
 	}
 
+	@Remote
+	public static interface D02 {
+
+		@RemoteMethod(method="POST",path="d01")
+		public Future<String> d01a(@Body String foo);
+
+		@RemoteMethod(method="POST",path="d01")
+		public Future<HttpResponse> d01b(@Body String foo);
+
+		@RemoteMethod(method="POST",path="d01")
+		public Future<Reader> d01c(@Body String foo);
+
+		@RemoteMethod(method="POST",path="d01")
+		public Future<InputStream> d01d(@Body String foo);
+	}
+
 	@Test
 	public void d01_returnTypes_partSerialization() throws Exception {
 		D01 t = MockRemote.build(D01.class, D.class, OpenApi.DEFAULT);
@@ -206,4 +285,13 @@ public class RemoteMethodAnnotationTest {
 		assertEquals("foo", IOUtils.read(t.d01c("foo")));
 		assertEquals("foo", IOUtils.read(t.d01d("foo")));
 	}
+
+	@Test
+	public void d02_returnTypes_partSerialization_futures() throws Exception {
+		D02 t = MockRemote.build(D02.class, D.class, OpenApi.DEFAULT);
+		assertEquals("foo", t.d01a("foo").get());
+		assertEquals("foo", IOUtils.read(t.d01b("foo").get().getEntity().getContent()));
+		assertEquals("foo", IOUtils.read(t.d01c("foo").get()));
+		assertEquals("foo", IOUtils.read(t.d01d("foo").get()));
+	}
 }
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java
index 2f5d6eb..7e68be1 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodReturn.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest.client.remote;
 
 import java.lang.reflect.*;
+import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.remote.RemoteMethod;
@@ -33,6 +34,7 @@ public final class RemoteMethodReturn {
 	private final Type returnType;
 	private final RemoteReturn returnValue;
 	private final ResponseBeanMeta meta;
+	private boolean isFuture;
 
 	@SuppressWarnings("deprecation")
 	RemoteMethodReturn(MethodInfo m) {
@@ -46,7 +48,13 @@ public final class RemoteMethodReturn {
 			rm = m.getResolvedReturnType().getLastAnnotation(RemoteMethod.class);
 
 		RemoteReturn rv = null;
-		if (rt.is(void.class))
+
+		if (rt.is(Future.class)) {
+			isFuture = true;
+			Type t = ((ParameterizedType)rt.innerType()).getActualTypeArguments()[0];
+			rt = ClassInfo.of(t);
+		}
+		if (rt.is(void.class) || rt.is(Void.class))
 			rv = RemoteReturn.NONE;
 		else if (orm != null)
 			switch (orm.returns()) {
@@ -67,7 +75,7 @@ public final class RemoteMethodReturn {
 			this.meta = null;
 		}
 
-		this.returnType = m.getReturnType().innerType();
+		this.returnType = rt.innerType();
 		this.returnValue = rv;
 	}
 
@@ -90,6 +98,15 @@ public final class RemoteMethodReturn {
 	}
 
 	/**
+	 * Returns <jk>true</jk> if the return is wrapped in a {@link Future}.
+	 *
+	 * @return <jk>true</jk> if the return is wrapped in a {@link Future}.
+	 */
+	public boolean isFuture() {
+		return isFuture;
+	}
+
+	/**
 	 * Specifies whether the return value is the body of the request or the HTTP status.
 	 *
 	 * @return The type of value returned.
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
index 16f0f3b..a3e12b9 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
@@ -2615,29 +2615,17 @@ public class RestClient extends BeanContext implements HttpClient, Closeable {
 							}
 
 							RemoteMethodReturn rmr = rmm.getReturns();
-							if (rmr.getReturnValue() == RemoteReturn.NONE) {
-								rc.complete();
-								return null;
-							} else if (rmr.getReturnValue() == RemoteReturn.STATUS) {
-								rc.ignoreErrors();
-								int returnCode = rc.complete().getStatusCode();
-								Class<?> rt = method.getReturnType();
-								if (rt == Integer.class || rt == int.class)
-									return returnCode;
-								if (rt == Boolean.class || rt == boolean.class)
-									return returnCode < 400;
-								throw new RestCallException("Invalid return type on method annotated with @RemoteMethod(returns=HTTP_STATUS).  Only integer and booleans types are valid.");
-							} else if (rmr.getReturnValue() == RemoteReturn.BEAN) {
-								rc.ignoreErrors();
-								return rc.run().as(rmr.getResponseBeanMeta());
-							} else {
-								rc.ignoreErrors();
-								Object v = rc.run().getBody().as(rmr.getReturnType());
-								if (v == null && method.getReturnType().isPrimitive())
-									v = ClassInfo.of(method.getReturnType()).getPrimitiveDefault();
-								return v;
+							if (rmr.isFuture()) {
+								return getExecutorService(true).submit(new Callable<Object>() {
+									@Override
+									public Object call() throws Exception {
+										return executeRemote(rc, method, rmr);
+									}
+								});
 							}
 
+							return executeRemote(rc, method, rmr);
+
 						} catch (RestCallException e) {
 							// Try to throw original exception if possible.
 							e.throwServerException(interfaceClass.getClassLoader(), rmm.getExceptions());
@@ -2652,6 +2640,31 @@ public class RestClient extends BeanContext implements HttpClient, Closeable {
 		}
 	}
 
+	Object executeRemote(RestRequest rc, Method method, RemoteMethodReturn rmr) throws RestCallException {
+		if (rmr.getReturnValue() == RemoteReturn.NONE) {
+			rc.complete();
+			return null;
+		} else if (rmr.getReturnValue() == RemoteReturn.STATUS) {
+			rc.ignoreErrors();
+			int returnCode = rc.complete().getStatusCode();
+			Class<?> rt = method.getReturnType();
+			if (rt == Integer.class || rt == int.class)
+				return returnCode;
+			if (rt == Boolean.class || rt == boolean.class)
+				return returnCode < 400;
+			throw new RestCallException("Invalid return type on method annotated with @RemoteMethod(returns=HTTP_STATUS).  Only integer and booleans types are valid.");
+		} else if (rmr.getReturnValue() == RemoteReturn.BEAN) {
+			rc.ignoreErrors();
+			return rc.run().as(rmr.getResponseBeanMeta());
+		} else {
+			rc.ignoreErrors();
+			Object v = rc.run().getBody().as(rmr.getReturnType());
+			if (v == null && method.getReturnType().isPrimitive())
+				v = ClassInfo.of(method.getReturnType()).getPrimitiveDefault();
+			return v;
+		}
+	}
+
 	/**
 	 * Create a new Remote Interface against a {@link RemoteInterface @RemoteInterface}-annotated class.
 	 *