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/05/24 23:06:08 UTC
[2/2] incubator-juneau git commit: New @RequestBean remoteable
annotation
New @RequestBean remoteable annotation
Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/788b8488
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/788b8488
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/788b8488
Branch: refs/heads/master
Commit: 788b848861d23b01de4c18d1667db62adcbb205b
Parents: cdebb74
Author: JamesBognar <ja...@apache.org>
Authored: Wed May 24 19:06:04 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Wed May 24 19:06:04 2017 -0400
----------------------------------------------------------------------
.../main/java/org/apache/juneau/BeanMeta.java | 4 +-
.../org/apache/juneau/BeanPropertyMeta.java | 31 +-
.../org/apache/juneau/internal/ClassUtils.java | 44 +
.../java/org/apache/juneau/remoteable/Body.java | 20 +-
.../org/apache/juneau/remoteable/FormData.java | 29 +-
.../apache/juneau/remoteable/FormDataIfNE.java | 2 +-
.../org/apache/juneau/remoteable/Header.java | 29 +-
.../apache/juneau/remoteable/HeaderIfNE.java | 2 +-
.../java/org/apache/juneau/remoteable/Path.java | 28 +-
.../org/apache/juneau/remoteable/Query.java | 31 +-
.../org/apache/juneau/remoteable/QueryIfNE.java | 2 +-
.../juneau/remoteable/RemoteableMethodMeta.java | 18 +-
.../apache/juneau/remoteable/RequestBean.java | 85 +
juneau-core/src/main/javadoc/overview.html | 12 +
.../org/apache/juneau/rest/client/RestCall.java | 2 +-
.../apache/juneau/rest/client/RestClient.java | 46 +
.../rest/test/ThirdPartyProxyResource.java | 92 +
.../juneau/rest/test/ThirdPartyProxyTest.java | 2279 +++++++++++++++---
.../org/apache/juneau/rest/RestContext.java | 16 +-
19 files changed, 2446 insertions(+), 326 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
index 6ca660c..cd1c0a6 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
@@ -194,7 +194,7 @@ public class BeanMeta<T> {
if (ctx.isNotABean(c))
return "Class matches exclude-class list";
- if (! cVis.isVisible(c.getModifiers()))
+ if (! (cVis.isVisible(c.getModifiers()) || c.isAnonymousClass()))
return "Class is not public";
if (c.isAnnotationPresent(BeanIgnore.class))
@@ -557,7 +557,7 @@ public class BeanMeta<T> {
Class<?>[] pt = m.getParameterTypes();
Class<?> rt = m.getReturnType();
boolean isGetter = false, isSetter = false;
- BeanProperty bp = m.getAnnotation(BeanProperty.class);
+ BeanProperty bp = getMethodAnnotation(BeanProperty.class, m);
String bpName = bp == null ? "" : bp.name();
if (pt.length == 0) {
if (n.startsWith("get") && (! rt.equals(Void.TYPE))) {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index 409373a..e520799 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -132,7 +132,7 @@ public class BeanPropertyMeta {
}
if (getter != null) {
- BeanProperty p = getter.getAnnotation(BeanProperty.class);
+ BeanProperty p = getMethodAnnotation(BeanProperty.class, getter);
if (rawTypeMeta == null)
rawTypeMeta = f.resolveClassMeta(p, getter.getGenericReturnType(), typeVarImpls);
isUri |= (rawTypeMeta.isUri() || getter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
@@ -146,7 +146,7 @@ public class BeanPropertyMeta {
}
if (setter != null) {
- BeanProperty p = setter.getAnnotation(BeanProperty.class);
+ BeanProperty p = getMethodAnnotation(BeanProperty.class, setter);
if (rawTypeMeta == null)
rawTypeMeta = f.resolveClassMeta(p, setter.getGenericParameterTypes()[0], typeVarImpls);
isUri |= (rawTypeMeta.isUri() || setter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
@@ -224,7 +224,7 @@ public class BeanPropertyMeta {
if (c == Null.class)
return null;
try {
- if (ClassUtils.isParentClass(PojoSwap.class, c)) {
+ if (isParentClass(PojoSwap.class, c)) {
return (PojoSwap)c.newInstance();
}
throw new RuntimeException("TODO - Surrogate swaps not yet supported.");
@@ -892,17 +892,38 @@ public class BeanPropertyMeta {
appendAnnotations(a, field.getType(), l);
}
if (getter != null) {
- addIfNotNull(l, getter.getAnnotation(a));
+ addIfNotNull(l, getMethodAnnotation(a, getter));
appendAnnotations(a, getter.getReturnType(), l);
}
if (setter != null) {
- addIfNotNull(l, setter.getAnnotation(a));
+ addIfNotNull(l, getMethodAnnotation(a, setter));
appendAnnotations(a, setter.getReturnType(), l);
}
+
appendAnnotations(a, this.getBeanMeta().getClassMeta().getInnerClass(), l);
return l;
}
+ /**
+ * Returns the specified annotation on the field or methods that define this property.
+ * <p>
+ * This method will search up the parent class/interface hierarchy chain to search for the annotation on
+ * overridden getters and setters.
+ *
+ * @param a The annotation to search for.
+ * @return The annotation, or <jk>null</jk> if it wasn't found.
+ */
+ public <A extends Annotation> A getAnnotation(Class<A> a) {
+ A t = null;
+ if (field != null)
+ t = field.getAnnotation(a);
+ if (t == null && getter != null)
+ t = getMethodAnnotation(a, getter);
+ if (t == null && setter != null)
+ t = getMethodAnnotation(a, setter);
+ return t;
+ }
+
private Object transform(BeanSession session, Object o) throws SerializeException {
// First use swap defined via @BeanProperty.
if (swap != null)
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 1f2788b..d176b92 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -13,6 +13,7 @@
package org.apache.juneau.internal;
import java.io.*;
+import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
@@ -329,6 +330,49 @@ public final class ClassUtils {
return Modifier.isPublic(c.getModifiers());
}
+ /**
+ * Returns the specified annotation on the specified method.
+ * <p>
+ * Similar to {@link Method#getAnnotation(Class)}, but searches up the parent hierarchy for the annotation
+ * defined on parent classes and interfaces.
+ * <p>
+ * Normally, annotations defined on methods of parent classes and interfaces are not inherited by the child
+ * methods.
+ * This utility method gets around that limitation by searching the class hierarchy for the "same" method.
+ *
+ * @param a The annotation to search for.
+ * @param m The method to search.
+ * @return The annotation, or <jk>null</jk> if it wasn't found.
+ */
+ public static <T extends Annotation> T getMethodAnnotation(Class<T> a, Method m) {
+ T t = m.getAnnotation(a);
+ if (t != null)
+ return t;
+ Class<?> pc = m.getDeclaringClass().getSuperclass();
+ if (pc != null) {
+ for (Method m2 : pc.getDeclaredMethods()) {
+ if (isSameMethod(m, m2)) {
+ t = getMethodAnnotation(a, m2);
+ if (t != null)
+ return t;
+ }
+ }
+ }
+ for (Class<?> ic : m.getDeclaringClass().getInterfaces()) {
+ for (Method m2 : ic.getDeclaredMethods()) {
+ if (isSameMethod(m, m2)) {
+ t = getMethodAnnotation(a, m2);
+ if (t != null)
+ return t;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean isSameMethod(Method m1, Method m2) {
+ return m1.getName().equals(m2.getName()) && Arrays.equals(m1.getParameterTypes(), m2.getParameterTypes());
+ }
/**
* Locates the no-arg constructor for the specified class.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
index a73b694..3266cfa 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Body.java
@@ -41,9 +41,27 @@ import org.apache.juneau.serializer.*;
* <li><code>HttpEntity</code> - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
* <li><code>NameValuePairs</code> - Converted to a URL-encoded FORM post.
* </ul>
+ * <p>
+ * The annotation can also be applied to a bean property field or getter when the argument is annotated with
+ * {@link RequestBean @RequestBean}:
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ * <jk>public interface</jk> MyProxy {
+ *
+ * <ja>@RemoteMethod</ja>(path=<js>"/mymethod"</js>)
+ * String myProxyMethod(<ja>@RequestBean</ja> MyRequestBean bean);
+ * }
+ *
+ * <jk>public interface</jk> MyRequestBean {
+ * <ja>@Body</ja>
+ * MyPojo getMyPojo();
+ * }
+ * </p>
*/
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface Body {}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
index 780e64d..23983c3 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.annotation.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -47,9 +48,35 @@ import org.apache.juneau.urlencoding.*;
* <li>A bean - Individual name-value pairs.
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* </ul>
+ * <p>
+ * The annotation can also be applied to a bean property field or getter when the argument is annotated with
+ * {@link RequestBean @RequestBean}:
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ * <jk>public interface</jk> MyProxy {
+ *
+ * <ja>@RemoteMethod</ja>(path=<js>"/mymethod"</js>)
+ * String myProxyMethod(<ja>@RequestBean</ja> MyRequestBean bean);
+ * }
+ *
+ * <jk>public interface</jk> MyRequestBean {
+ * <ja>@FormData</ja>
+ * String getFoo();
+ *
+ * <ja>@FormData</ja>
+ * MyPojo getBar();
+ * }
+ * </p>
+ * <p>
+ * When used in a request bean, the {@link #value()} can be used to override the form data parameter name.
+ * It can also be overridden via the {@link BeanProperty#name @BeanProperty.name()} annotation.
+ * A name of <js>"*"</js> where the bean property value is a map or bean will cause the individual entries in the
+ * map or bean to be expanded to form data parameters.
*/
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface FormData {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
index e38ac6a..d043217 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
@@ -21,7 +21,7 @@ import java.lang.annotation.*;
* Identical to {@link FormData @FormData} except skips values if they're null/blank.
*/
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface FormDataIfNE {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
index d414eb1..3fae979 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.annotation.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -44,9 +45,35 @@ import org.apache.juneau.urlencoding.*;
* <li>A bean - Individual name-value pairs.
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* </ul>
+ * <p>
+ * The annotation can also be applied to a bean property field or getter when the argument is annotated with
+ * {@link RequestBean @RequestBean}:
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ * <jk>public interface</jk> MyProxy {
+ *
+ * <ja>@RemoteMethod</ja>(path=<js>"/mymethod"</js>)
+ * String myProxyMethod(<ja>@RequestBean</ja> MyRequestBean bean);
+ * }
+ *
+ * <jk>public interface</jk> MyRequestBean {
+ * <ja>@Header</ja>(<js>"Foo"</js>)
+ * String getFoo();
+ *
+ * <ja>@Header</ja>(<js>"Bar"</js>)
+ * MyPojo getBar();
+ * }
+ * </p>
+ * <p>
+ * When used in a request bean, the {@link #value()} can be used to override the header name.
+ * It can also be overridden via the {@link BeanProperty#name @BeanProperty.name()} annotation.
+ * A name of <js>"*"</js> where the bean property value is a map or bean will cause the individual entries in the
+ * map or bean to be expanded to headers.
*/
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface Header {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
index 350ddcf..2aaf711 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
@@ -21,7 +21,7 @@ import java.lang.annotation.*;
* Identical to {@link Header @Header} except skips values if they're null/blank.
*/
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface HeaderIfNE {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
index 0420e54..47c6eea 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.annotation.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -41,9 +42,32 @@ import org.apache.juneau.urlencoding.*;
* <li>A bean - Individual name-value pairs.
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* </ul>
-*/
+ * <p>
+ * The annotation can also be applied to a bean property field or getter when the argument is annotated with
+ * {@link RequestBean @RequestBean}:
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ * <jk>public interface</jk> MyProxy {
+ *
+ * <ja>@RemoteMethod</ja>(path=<js>"/mymethod1/{foo}"</js>)
+ * String myProxyMethod(<ja>@RequestBean</ja> MyRequestBean bean);
+ * }
+ *
+ * <jk>public interface</jk> MyRequestBean {
+ * <ja>@Path</ja>
+ * String getFoo();
+ * }
+ * </p>
+ * <p>
+ * When used in a request bean, the {@link #value()} can be used to override the path variable name.
+ * It can also be overridden via the {@link BeanProperty#name @BeanProperty.name()} annotation.
+ * A name of <js>"*"</js> where the bean property value is a map or bean will cause the individual entries in the
+ * map or bean to be expanded to path variables.
+ */
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface Path {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
index bd7e4e7..5729412 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
@@ -17,6 +17,7 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
+import org.apache.juneau.annotation.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -48,9 +49,35 @@ import org.apache.juneau.urlencoding.*;
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* <li>{@link String} - Treated as a query string.
* </ul>
-*/
+ * <p>
+ * The annotation can also be applied to a bean property field or getter when the argument is annotated with
+ * {@link RequestBean @RequestBean}:
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ * <jk>public interface</jk> MyProxy {
+ *
+ * <ja>@RemoteMethod</ja>(path=<js>"/mymethod"</js>)
+ * String myProxyMethod(<ja>@RequestBean</ja> MyRequestBean bean);
+ * }
+ *
+ * <jk>public interface</jk> MyRequestBean {
+ * <ja>@Query</ja>
+ * String getFoo();
+ *
+ * <ja>@Query</ja>
+ * MyPojo getBar();
+ * }
+ * </p>
+ * <p>
+ * When used in a request bean, the {@link #value()} can be used to override the query parameter name.
+ * It can also be overridden via the {@link BeanProperty#name @BeanProperty.name()} annotation.
+ * A name of <js>"*"</js> where the bean property value is a map or bean will cause the individual entries in the
+ * map or bean to be expanded to query parameters.
+ */
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface Query {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
index 59ac3d2..c2cc2fd 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
@@ -21,7 +21,7 @@ import java.lang.annotation.*;
* Identical to {@link Query @Query} except skips values if they're null/blank.
*/
@Documented
-@Target(PARAMETER)
+@Target({PARAMETER,FIELD,METHOD})
@Retention(RUNTIME)
@Inherited
public @interface QueryIfNE {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
index 9504d37..bad4686 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
@@ -30,7 +30,7 @@ public class RemoteableMethodMeta {
private final String httpMethod;
private final String url;
private final RemoteMethodArg[] pathArgs, queryArgs, headerArgs, formDataArgs;
- private final Integer[] otherArgs;
+ private final Integer[] requestBeanArgs, otherArgs;
private final Integer bodyArg;
/**
@@ -47,6 +47,7 @@ public class RemoteableMethodMeta {
this.queryArgs = b.queryArgs.toArray(new RemoteMethodArg[b.queryArgs.size()]);
this.formDataArgs = b.formDataArgs.toArray(new RemoteMethodArg[b.formDataArgs.size()]);
this.headerArgs = b.headerArgs.toArray(new RemoteMethodArg[b.headerArgs.size()]);
+ this.requestBeanArgs = b.requestBeanArgs.toArray(new Integer[b.requestBeanArgs.size()]);
this.otherArgs = b.otherArgs.toArray(new Integer[b.otherArgs.size()]);
this.bodyArg = b.bodyArg;
}
@@ -58,7 +59,9 @@ public class RemoteableMethodMeta {
queryArgs = new LinkedList<RemoteMethodArg>(),
headerArgs = new LinkedList<RemoteMethodArg>(),
formDataArgs = new LinkedList<RemoteMethodArg>();
- private List<Integer> otherArgs = new LinkedList<Integer>();
+ private List<Integer>
+ requestBeanArgs = new LinkedList<Integer>(),
+ otherArgs = new LinkedList<Integer>();
private Integer bodyArg;
private Builder(String restUrl, Method m) {
@@ -106,6 +109,9 @@ public class RemoteableMethodMeta {
} else if (ca == HeaderIfNE.class) {
HeaderIfNE h = (HeaderIfNE)a;
annotated = headerArgs.add(new RemoteMethodArg(h.value(), index, true));
+ } else if (ca == RequestBean.class) {
+ annotated = true;
+ requestBeanArgs.add(index);
} else if (ca == Body.class) {
annotated = true;
if (bodyArg == null)
@@ -173,6 +179,14 @@ public class RemoteableMethodMeta {
}
/**
+ * Returns the {@link RequestBean @RequestBean} annotated arguments on this Java method.
+ * @return A list of zero-indexed argument indices.
+ */
+ public Integer[] getRequestBeanArgs() {
+ return requestBeanArgs;
+ }
+
+ /**
* Returns the remaining non-annotated arguments on this Java method.
* @return A list of zero-indexed argument indices.
*/
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-core/src/main/java/org/apache/juneau/remoteable/RequestBean.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/RequestBean.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/RequestBean.java
new file mode 100644
index 0000000..3da7f6c
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RequestBean.java
@@ -0,0 +1,85 @@
+// ***************************************************************************************************************************
+// * 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.remoteable;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Annotation applied to Java method arguments of interface proxies to denote a bean with remoteable annotations.
+ * <p>
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
+ * <jk>public interface</jk> MyProxy {
+ *
+ * <ja>@RemoteMethod</ja>(path=<js>"/mymethod/{p1}/{p2}"</js>)
+ * String myProxyMethod(<ja>@RequestBean</ja> MyRequestBean bean);
+ * }
+ *
+ * <jk>public interface</jk> MyRequestBean {
+ *
+ * <ja>@Path</ja>
+ * String getP1();
+ *
+ * <ja>@Path</ja>(<js>"p2"</js>)
+ * String getX();
+ *
+ * <ja>@Query</ja>
+ * String getQ1();
+ *
+ * <ja>@Query</ja>
+ * <ja>@BeanProperty</ja>(name=<js>"q2"</js>)
+ * String getQuery2();
+ *
+ * <ja>@QueryIfNE</ja>(<js>"q3"</js>)
+ * String getQuery3();
+ *
+ * <ja>@QueryIfNE</ja>
+ * Map<String,Object> getExtraQueries();
+ *
+ * <ja>@FormData</ja>
+ * String getF1();
+ *
+ * <ja>@FormData</ja>
+ * <ja>@BeanProperty</ja>(name=<js>"f2"</js>)
+ * String getFormData2();
+ *
+ * <ja>@FormDataIfNE</ja>(<js>"f3"</js>)
+ * String getFormData3();
+ *
+ * <ja>@FormDataIfNE</ja>
+ * Map<String,Object> getExtraFormData();
+ *
+ * <ja>@Header</ja>
+ * String getH1();
+ *
+ * <ja>@Header</ja>
+ * <ja>@BeanProperty</ja>(name=<js>"H2"</js>)
+ * String getHeader2();
+ *
+ * <ja>@HeaderIfNE</ja>(<js>"H3"</js>)
+ * String getHeader3();
+ *
+ * <ja>@HeaderIfNE</ja>
+ * Map<String,Object> getExtraHeaders();
+ * }
+ * </p>
+ */
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface RequestBean {}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/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 ff4ce10..f5b4f86 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6126,6 +6126,16 @@
<li>New {@link org.apache.juneau.html.annotation.Html#render() @Html.render()} annotation and {@link org.apache.juneau.html.HtmlRender} class that allows you
to customize the HTML output and CSS style on bean properties:<br>
<img class='bordered' src='doc-files/HtmlRender_1.png'>
+ <li>{@link org.apache.juneau.annotation.BeanProperty @BeanProperty} annotation can now be applied to getters
+ and setters defined on interfaces.
+ <li>New methods on {@link org.apache.juneau.serializer.SerializerSession} and {@link org.apache.juneau.parser.ParserSession}
+ for retrieving context and runtime-override properties:
+ <ul>
+ <li>{@link org.apache.juneau.Session#getProperty(String)}
+ <li>{@link org.apache.juneau.Session#getProperty(String,String)}
+ <li>{@link org.apache.juneau.Session#getProperty(Class,String)}
+ <li>{@link org.apache.juneau.Session#getProperty(Class,String,Object)}
+ </ul>
</ul>
<h6 class='topic'>org.apache.juneau.rest</h6>
@@ -6254,6 +6264,8 @@
<h6 class='topic'>org.apache.juneau.rest.client</h6>
<ul class='spaced-list'>
<li>New {@link org.apache.juneau.remoteable.Path @Path} annotation for specifying path variables on remoteable interfaces.
+ <li>New {@link org.apache.juneau.remoteable.RequestBean @RequestBean} annotation for specifying beans with remoteable annotations
+ defined on properties.
<li>The following annotations (and related methods on RestCall) can now take <code>NameValuePairs</code> and beans as input
when using <js>"*"</js> as the name.
<br>{@link org.apache.juneau.remoteable.FormData @FormData},{@link org.apache.juneau.remoteable.FormDataIfNE @FormDataIfNE},
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/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 14c7207..f06a172 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
@@ -198,7 +198,7 @@ public final class RestCall {
} else if (isBean(value)){
return query(name, toBeanMap(value), skipIfEmpty);
} else {
- throw new RuntimeException("Invalid name passed to query(name,value,skipIfEmpty).");
+ throw new RuntimeException("Invalid name passed to query(name,value,skipIfEmpty): ("+name+","+value+","+skipIfEmpty+")");
}
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/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 b026ff4..dbcc294 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
@@ -552,6 +552,46 @@ public class RestClient extends CoreObject {
if (rmm.getBodyArg() != null)
rc.input(args[rmm.getBodyArg()]);
+ if (rmm.getRequestBeanArgs().length > 0) {
+ BeanSession bs = getBeanContext().createSession();
+
+ for (Integer i : rmm.getRequestBeanArgs()) {
+ BeanMap<?> bm = bs.toBeanMap(args[i]);
+ for (BeanPropertyValue bpv : bm.getValues(true)) {
+ BeanPropertyMeta pMeta = bpv.getMeta();
+ Object val = bpv.getValue();
+
+ Path p = pMeta.getAnnotation(Path.class);
+ if (p != null)
+ rc.path(getName(p.value(), pMeta), val);
+
+ Query q1 = pMeta.getAnnotation(Query.class);
+ if (q1 != null)
+ rc.query(getName(q1.value(), pMeta), val, false);
+
+ QueryIfNE q2 = pMeta.getAnnotation(QueryIfNE.class);
+ if (q2 != null)
+ rc.query(getName(q2.value(), pMeta), val, true);
+
+ FormData f1 = pMeta.getAnnotation(FormData.class);
+ if (f1 != null)
+ rc.formData(getName(f1.value(), pMeta), val, false);
+
+ FormDataIfNE f2 = pMeta.getAnnotation(FormDataIfNE.class);
+ if (f2 != null)
+ rc.formData(getName(f2.value(), pMeta), val, true);
+
+ org.apache.juneau.remoteable.Header h1 = pMeta.getAnnotation(org.apache.juneau.remoteable.Header.class);
+ if (h1 != null)
+ rc.header(getName(h1.value(), pMeta), val, false);
+
+ HeaderIfNE h2 = pMeta.getAnnotation(HeaderIfNE.class);
+ if (h2 != null)
+ rc.header(getName(h2.value(), pMeta), val, true);
+ }
+ }
+ }
+
if (rmm.getOtherArgs().length > 0) {
Object[] otherArgs = new Object[rmm.getOtherArgs().length];
int i = 0;
@@ -576,6 +616,12 @@ public class RestClient extends CoreObject {
}
}
+ private static String getName(String name, BeanPropertyMeta pMeta) {
+ if ("*".equals(name) && ! pMeta.getClassMeta().isMapOrBean())
+ name = pMeta.getName();
+ return name;
+ }
+
private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*");
UrlEncodingSerializer getUrlEncodingSerializer() {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/788b8488/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
index 4a48379..532f292 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
@@ -1032,7 +1032,99 @@ public class ThirdPartyProxyResource extends ResourceJena {
return "OK";
}
+ //--------------------------------------------------------------------------------
+ // RequestBean tests
+ //--------------------------------------------------------------------------------
+
+ @RestMethod(name="POST", path="/reqBeanPath/{a}/{b}")
+ public String reqBeanPath(
+ @Path("a") int a,
+ @Path("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/reqBeanQuery")
+ public String reqBeanQuery(
+ @Query("a") int a,
+ @Query("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/reqBeanQueryIfNE")
+ public String reqBeanQueryIfNE(
+ @Query("a") String a,
+ @Query("b") String b,
+ @Query("c") String c
+ ) throws Exception {
+
+ assertEquals("foo", a);
+ assertNull(b);
+ assertNull(c);
+
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/reqBeanFormData")
+ public String reqBeanFormData(
+ @FormData("a") int a,
+ @FormData("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/reqBeanFormDataIfNE")
+ public String reqBeanFormDataIfNE(
+ @FormData("a") String a,
+ @FormData("b") String b,
+ @FormData("c") String c
+ ) throws Exception {
+
+ assertEquals("foo", a);
+ assertNull(b);
+ assertNull(c);
+
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/reqBeanHeader")
+ public String reqBeanHeader(
+ @Header("a") int a,
+ @Header("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/reqBeanHeaderIfNE")
+ public String reqBeanHeaderIfNE(
+ @Header("a") String a,
+ @Header("b") String b,
+ @Header("c") String c
+ ) throws Exception {
+
+ assertEquals("foo", a);
+ assertNull(b);
+ assertNull(c);
+
+ return "OK";
+ }
//--------------------------------------------------------------------------------
// Test return types.
//--------------------------------------------------------------------------------