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 <[email protected]>
Authored: Wed May 24 19:06:04 2017 -0400
Committer: JamesBognar <[email protected]>
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&lt;String,Object&gt; 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&lt;String,Object&gt; 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&lt;String,Object&gt; 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.
        
//--------------------------------------------------------------------------------

Reply via email to