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/21 09:01:25 UTC
incubator-juneau git commit: Support for @Query("*") and @Path on
remoteable interfaces.
Repository: incubator-juneau
Updated Branches:
refs/heads/master a3d7cc9af -> 3c1ff80f9
Support for @Query("*") and @Path on remoteable interfaces.
Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/3c1ff80f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/3c1ff80f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/3c1ff80f
Branch: refs/heads/master
Commit: 3c1ff80f95a441a0358b403f9a4a9cb8e70f5209
Parents: a3d7cc9
Author: JamesBognar <ja...@apache.org>
Authored: Sun May 21 05:01:21 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Sun May 21 05:01:21 2017 -0400
----------------------------------------------------------------------
.../java/org/apache/juneau/BeanContext.java | 12 +
.../org/apache/juneau/remoteable/FormData.java | 13 +-
.../apache/juneau/remoteable/FormDataIfNE.java | 11 +-
.../org/apache/juneau/remoteable/Header.java | 14 +-
.../apache/juneau/remoteable/HeaderIfNE.java | 11 +-
.../java/org/apache/juneau/remoteable/Path.java | 63 ++++++
.../org/apache/juneau/remoteable/Query.java | 15 +-
.../org/apache/juneau/remoteable/QueryIfNE.java | 12 +-
.../juneau/remoteable/RemoteableMethodMeta.java | 17 +-
juneau-core/src/main/javadoc/overview.html | 6 +
.../org/apache/juneau/rest/client/RestCall.java | 226 +++++++++++++------
.../apache/juneau/rest/client/RestClient.java | 5 +
.../rest/test/ThirdPartyProxyResource.java | 92 ++++++++
.../juneau/rest/test/ThirdPartyProxyTest.java | 93 ++++++++
14 files changed, 505 insertions(+), 85 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
index 33c1165..5b1f166 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java
@@ -1000,6 +1000,18 @@ public class BeanContext extends Context {
}
/**
+ * Returns <jk>true</jk> if the specified object is a bean.
+ *
+ * @param o The object to test.
+ * @return <jk>true</jk> if the specified object is a bean. <jk>false</jk> if the bean is <jk>null</jk>.
+ */
+ public boolean isBean(Object o) {
+ if (o == null)
+ return false;
+ return getClassMetaForObject(o).isBean();
+ }
+
+ /**
* Prints meta cache statistics to <code>System.out</code>.
*/
protected static void dumpCacheStats() {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 5d0ecf2..780e64d 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
@@ -44,6 +44,8 @@ import org.apache.juneau.urlencoding.*;
* <li><code>NameValuePairs</code> - Individual name-value pairs.
* <li><code>Map<String,Object></code> - Individual name-value pairs.
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * <li>A bean - Individual name-value pairs.
+ * Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* </ul>
*/
@Documented
@@ -54,7 +56,14 @@ public @interface FormData {
/**
* The form post parameter name.
- * Can be blank if the value is an instance of <code>NameValuePairs</code> or <code>Map<String,Object></code>.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
*/
- String value() default "";
+ String value() default "*";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 a156928..e38ac6a 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
@@ -28,7 +28,14 @@ public @interface FormDataIfNE {
/**
* The form post parameter name.
- * Can be blank if the value is an instance of <code>NameValuePairs</code> or <code>Map<String,Object></code>.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
*/
- String value() default "";
+ String value() default "*";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 31eab82..d414eb1 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
@@ -37,9 +37,12 @@ import org.apache.juneau.urlencoding.*;
* <p>
* The argument can be any of the following types:
* <ul class='spaced-list'>
+ * <li><code>NameValuePairs</code> - Individual name-value pairs.
* <li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* <li><code>Map<String,Object></code> - Individual name-value pairs.
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * <li>A bean - Individual name-value pairs.
+ * Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* </ul>
*/
@Documented
@@ -50,7 +53,14 @@ public @interface Header {
/**
* The HTTP header name.
- * Can be blank if the value is an instance of <code>Map<String,Object></code>.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
*/
- String value() default "";
+ String value() default "*";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 e99915e..350ddcf 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
@@ -28,7 +28,14 @@ public @interface HeaderIfNE {
/**
* The HTTP header name.
- * Can be blank if the value is an instance of <code>Map<String,Object></code>.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
*/
- String value();
+ String value() default "*";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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
new file mode 100644
index 0000000..0420e54
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
@@ -0,0 +1,63 @@
+// ***************************************************************************************************************************
+// * 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.*;
+
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Annotation applied to Java method arguments of interface proxies to denote that they are path variables on the request.
+ * <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 myProxyMethod1(<ja>@Path</ja>(<js>"foo"</js>)</ja> String foo);
+ * }
+ * </p>
+ * <p>
+ * The argument can be any of the following types:
+ * <ul class='spaced-list'>
+ * <li><code>NameValuePairs</code> - Individual name-value pairs.
+ * <li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * <li><code>Map<String,Object></code> - Individual name-value pairs.
+ * Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * <li>A bean - Individual name-value pairs.
+ * Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * </ul>
+*/
+@Documented
+@Target(PARAMETER)
+@Retention(RUNTIME)
+@Inherited
+public @interface Path {
+
+ /**
+ * The path parameter name.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
+ */
+ String value() default "*";
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 0a82529..bd7e4e7 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
@@ -40,9 +40,12 @@ import org.apache.juneau.urlencoding.*;
* <p>
* The argument can be any of the following types:
* <ul class='spaced-list'>
+ * <li><code>NameValuePairs</code> - Individual name-value pairs.
* <li>Any serializable POJO - Converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* <li><code>Map<String,Object></code> - Individual name-value pairs.
* Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ * <li>A bean - Individual name-value pairs.
+ * Values are converted to text using {@link UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
* <li>{@link String} - Treated as a query string.
* </ul>
*/
@@ -54,7 +57,15 @@ public @interface Query {
/**
* The query parameter name.
- * Can be blank if the value is an instance of <code>Map<String,Object></code> or <code>String</code>.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>String</code> - A complete query string.
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
*/
- String value() default "";
+ String value() default "*";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 10ae263..59ac3d2 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
@@ -28,7 +28,15 @@ public @interface QueryIfNE {
/**
* The query parameter name.
- * Can be blank if the value is an instance of <code>Map<String,Object></code> or <code>String</code>.
+ * <p>
+ * A value of <js>"*"</js> indicates the value should be serialized as name/value pairs and is applicable
+ * for the following data types:
+ * <ul>
+ * <li><code>String</code> - A complete query string.
+ * <li><code>NameValuePairs</code>
+ * <li><code>Map<String,Object></code>
+ * <li>A bean
+ * </ul>
*/
- String value();
+ String value() default "*";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 12b0d6b..d48d42f 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
@@ -29,7 +29,7 @@ public class RemoteableMethodMeta {
private final String httpMethod;
private final String url;
- private final RemoteMethodArg[] queryArgs, headerArgs, formDataArgs;
+ private final RemoteMethodArg[] pathArgs, queryArgs, headerArgs, formDataArgs;
private final Integer[] otherArgs;
private final Integer bodyArg;
@@ -43,6 +43,7 @@ public class RemoteableMethodMeta {
Builder b = new Builder(restUrl, m);
this.httpMethod = b.httpMethod;
this.url = b.url;
+ this.pathArgs = b.pathArgs.toArray(new RemoteMethodArg[b.pathArgs.size()]);
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()]);
@@ -53,6 +54,7 @@ public class RemoteableMethodMeta {
private static class Builder {
private String httpMethod, url;
private List<RemoteMethodArg>
+ pathArgs = new LinkedList<RemoteMethodArg>(),
queryArgs = new LinkedList<RemoteMethodArg>(),
headerArgs = new LinkedList<RemoteMethodArg>(),
formDataArgs = new LinkedList<RemoteMethodArg>();
@@ -83,7 +85,10 @@ public class RemoteableMethodMeta {
boolean annotated = false;
for (Annotation a : aa) {
Class<?> ca = a.annotationType();
- if (ca == Query.class) {
+ if (ca == Path.class) {
+ Path p = (Path)a;
+ annotated = pathArgs.add(new RemoteMethodArg(p.value(), index, false));
+ } else if (ca == Query.class) {
Query q = (Query)a;
annotated = queryArgs.add(new RemoteMethodArg(q.value(), index, false));
} else if (ca == QueryIfNE.class) {
@@ -136,6 +141,14 @@ public class RemoteableMethodMeta {
}
/**
+ * Returns the {@link Path @Path} annotated arguments on this Java method.
+ * @return A map of {@link Path#value() @Path.value()} names to zero-indexed argument indices.
+ */
+ public RemoteMethodArg[] getPathArgs() {
+ return pathArgs;
+ }
+
+ /**
* Returns the {@link Query @Query} annotated arguments on this Java method.
* @return A map of {@link Query#value() @Query.value()} names to zero-indexed argument indices.
*/
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 7a90651..ff4ce10 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6253,6 +6253,12 @@
<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>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},
+ {@link org.apache.juneau.remoteable.Query @Query},{@link org.apache.juneau.remoteable.QueryIfNE @QueryIfNE},
+ {@link org.apache.juneau.remoteable.Header @Header},{@link org.apache.juneau.remoteable.HeaderIfNE @HeaderIfNE},
</ul>
<h6 class='topic'>org.apache.juneau.microservice</h6>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 5cdcd84..147b9e4 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
@@ -172,29 +172,33 @@ public final class RestCall {
* Adds a query parameter to the URI query.
*
* @param name The parameter name.
- * Can be null/blank if the value is a {@link Map} or {@link String}.
+ * Can be null/blank/* if the value is a {@link Map}, {@link String}, {@link NameValuePairs}, or bean.
* @param value The parameter value converted to a string using UON notation.
- * Can also be a {@link Map} or {@link String} if the name is null/blank.
- * If a {@link String} and the name is null/blank, then calls {@link URIBuilder#setCustomQuery(String)}.
+ * Can also be {@link Map}, {@link String}, {@link NameValuePairs}, or bean if the name is null/blank/*.
+ * If a {@link String} and the name is null/blank/*, then calls {@link URIBuilder#setCustomQuery(String)}.
* @param skipIfEmpty Don't add the pair if the value is empty.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
@SuppressWarnings("unchecked")
- public RestCall query(String name, Object value, boolean skipIfEmpty) {
- if (! isEmpty(name)) {
+ public RestCall query(String name, Object value, boolean skipIfEmpty) throws RestCallException {
+ if (! ("*".equals(name) || isEmpty(name))) {
if (! (isEmpty(value) && skipIfEmpty))
uriBuilder.addParameter(name, client.getUrlEncodingSerializer().serializePart(value, false, null));
+ } else if (value instanceof NameValuePairs) {
+ for (NameValuePair p : (NameValuePairs)value)
+ query(p.getName(), p.getValue(), skipIfEmpty);
+ } else if (value instanceof String) {
+ String s = value.toString();
+ if (! isEmpty(s))
+ uriBuilder.setCustomQuery(s);
+ } else if (value instanceof Map) {
+ for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
+ query(p.getKey(), p.getValue(), skipIfEmpty);
+ } else if (isBean(value)){
+ return query(name, toBeanMap(value));
} else {
- if (value instanceof String) {
- String s = value.toString();
- if (! isEmpty(s))
- uriBuilder.setCustomQuery(s);
- } else if (value instanceof Map) {
- for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- query(p.getKey(), p.getValue(), skipIfEmpty);
- } else {
- throw new RuntimeException("Invalid name passed to query(name,value,skipIfEmpty).");
- }
+ throw new RuntimeException("Invalid name passed to query(name,value,skipIfEmpty).");
}
return this;
}
@@ -264,28 +268,29 @@ public final class RestCall {
* Adds a form data pair to this request to perform a URL-encoded form post.
*
* @param name The parameter name.
- * Can be null/blank if the value is a {@link Map} or {@link NameValuePairs}.
+ * Can be null/blank/* if the value is a {@link Map}, {@link NameValuePairs}, or bean.
* @param value The parameter value converted to a string using UON notation.
- * Can also be a {@link Map} or {@link NameValuePairs}.
+ * Can also be {@link Map}, {@link NameValuePairs}, or bean if the name is null/blank/*.
* @param skipIfEmpty Don't add the pair if the value is empty.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
@SuppressWarnings("unchecked")
- public RestCall formData(String name, Object value, boolean skipIfEmpty) {
+ public RestCall formData(String name, Object value, boolean skipIfEmpty) throws RestCallException {
if (formData == null)
formData = new NameValuePairs();
- if (! isEmpty(name)) {
+ if (! ("*".equals(name) || isEmpty(name))) {
if (! (isEmpty(value) && skipIfEmpty))
formData.add(new SerializedNameValuePair(name, value, client.getUrlEncodingSerializer()));
+ } else if (value instanceof NameValuePairs) {
+ formData.addAll((NameValuePairs)value);
+ } else if (value instanceof Map) {
+ for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
+ formData(p.getKey(), p.getValue(), skipIfEmpty);
+ } else if (isBean(value)) {
+ return formData(name, toBeanMap(value));
} else {
- if (value instanceof NameValuePairs) {
- formData.addAll((NameValuePairs)value);
- } else if (value instanceof Map) {
- for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- formData(p.getKey(), p.getValue(), skipIfEmpty);
- } else {
- throw new RuntimeException("Invalid name passed to formData(name,value,skipIfEmpty).");
- }
+ throw new RuntimeException("Invalid name passed to formData(name,value,skipIfEmpty).");
}
return this;
}
@@ -354,6 +359,37 @@ public final class RestCall {
}
/**
+ * Replaces a variable of the form <js>"{name}"</js> in the URL path with the specified value.
+ * @param name The path variable name.
+ * @param value The replacement value.
+ *
+ * @return This object (for method chaining).
+ * @throws RestCallException If variable could not be found in path.
+ */
+ @SuppressWarnings("unchecked")
+ public RestCall path(String name, Object value) throws RestCallException {
+ String path = uriBuilder.getPath();
+ if (! ("*".equals(name) || isEmpty(name))) {
+ String var = "{" + name + "}";
+ if (path.indexOf(var) == -1)
+ throw new RestCallException("Path variable {"+name+"} was not found in path.");
+ String newPath = path.replace(var, client.getUrlEncodingSerializer().serializePart(value, false, null));
+ uriBuilder.setPath(newPath);
+ } else if (value instanceof NameValuePairs) {
+ for (NameValuePair p : (NameValuePairs)value)
+ path(p.getName(), p.getValue());
+ } else if (value instanceof Map) {
+ for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
+ path(p.getKey(), p.getValue());
+ } else if (isBean(value)) {
+ return path(name, toBeanMap(value));
+ } else {
+ throw new RuntimeException("Invalid name passed to path(name,value).");
+ }
+ return this;
+ }
+
+ /**
* Sets the URI user info.
*
* @param userInfo The new URI user info.
@@ -436,19 +472,23 @@ public final class RestCall {
* @param value The header value.
* @param skipIfEmpty Don't add the header if the name is null/empty.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
@SuppressWarnings("unchecked")
- public RestCall header(String name, Object value, boolean skipIfEmpty) {
- if (! isEmpty(name)) {
+ public RestCall header(String name, Object value, boolean skipIfEmpty) throws RestCallException {
+ if (! ("*".equals(name) || isEmpty(name))) {
if (! (isEmpty(value) && skipIfEmpty))
request.setHeader(name, client.getUrlEncodingSerializer().serializePart(value, false, true));
+ } else if (value instanceof NameValuePairs) {
+ for (NameValuePair p : (NameValuePairs)value)
+ header(p.getName(), p.getValue(), skipIfEmpty);
+ } else if (value instanceof Map) {
+ for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
+ header(p.getKey(), p.getValue(), skipIfEmpty);
+ } else if (isBean(value)) {
+ return header(name, toBeanMap(value), skipIfEmpty);
} else {
- if (value instanceof Map) {
- for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet())
- header(p.getKey(), p.getValue(), skipIfEmpty);
- } else {
- throw new RuntimeException("Invalid name passed to formData(name,value,skipIfEmpty).");
- }
+ throw new RuntimeException("Invalid name passed to header(name,value,skipIfEmpty).");
}
return this;
}
@@ -461,8 +501,9 @@ public final class RestCall {
* The name can be null/empty if the value is a {@link Map}.
* @param value The header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall header(String name, Object value) {
+ public RestCall header(String name, Object value) throws RestCallException {
return header(name, value, false);
}
@@ -471,8 +512,9 @@ public final class RestCall {
*
* @param values The header values.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall headers(Map<String,Object> values) {
+ public RestCall headers(Map<String,Object> values) throws RestCallException {
return header(null, values, false);
}
@@ -485,8 +527,9 @@ public final class RestCall {
* The name can be null/empty if the value is a {@link Map}.
* @param value The header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall headerIfNE(String name, Object value) {
+ public RestCall headerIfNE(String name, Object value) throws RestCallException {
return header(name, value, true);
}
@@ -497,8 +540,9 @@ public final class RestCall {
*
* @param values The header values.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall headersIfNE(Map<String,Object> values) {
+ public RestCall headersIfNE(Map<String,Object> values) throws RestCallException {
return header(null, values, true);
}
@@ -509,8 +553,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall accept(Object value) {
+ public RestCall accept(Object value) throws RestCallException {
return header("Accept", value);
}
@@ -521,8 +566,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall acceptCharset(Object value) {
+ public RestCall acceptCharset(Object value) throws RestCallException {
return header("Accept-Charset", value);
}
@@ -533,8 +579,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall acceptEncoding(Object value) {
+ public RestCall acceptEncoding(Object value) throws RestCallException {
return header("Accept-Encoding", value);
}
@@ -545,8 +592,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall acceptLanguage(Object value) {
+ public RestCall acceptLanguage(Object value) throws RestCallException {
return header("Accept-Language", value);
}
@@ -557,8 +605,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall authorization(Object value) {
+ public RestCall authorization(Object value) throws RestCallException {
return header("Authorization", value);
}
@@ -569,8 +618,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall cacheControl(Object value) {
+ public RestCall cacheControl(Object value) throws RestCallException {
return header("Cache-Control", value);
}
@@ -581,8 +631,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall connection(Object value) {
+ public RestCall connection(Object value) throws RestCallException {
return header("Connection", value);
}
@@ -593,8 +644,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall contentLength(Object value) {
+ public RestCall contentLength(Object value) throws RestCallException {
return header("Content-Length", value);
}
@@ -605,8 +657,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall contentType(Object value) {
+ public RestCall contentType(Object value) throws RestCallException {
return header("Content-Type", value);
}
@@ -617,8 +670,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall date(Object value) {
+ public RestCall date(Object value) throws RestCallException {
return header("Date", value);
}
@@ -629,8 +683,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall expect(Object value) {
+ public RestCall expect(Object value) throws RestCallException {
return header("Expect", value);
}
@@ -641,8 +696,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall forwarded(Object value) {
+ public RestCall forwarded(Object value) throws RestCallException {
return header("Forwarded", value);
}
@@ -653,8 +709,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall from(Object value) {
+ public RestCall from(Object value) throws RestCallException {
return header("From", value);
}
@@ -665,8 +722,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall host(Object value) {
+ public RestCall host(Object value) throws RestCallException {
return header("Host", value);
}
@@ -677,8 +735,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall ifMatch(Object value) {
+ public RestCall ifMatch(Object value) throws RestCallException {
return header("If-Match", value);
}
@@ -689,8 +748,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall ifModifiedSince(Object value) {
+ public RestCall ifModifiedSince(Object value) throws RestCallException {
return header("If-Modified-Since", value);
}
@@ -701,8 +761,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall ifNoneMatch(Object value) {
+ public RestCall ifNoneMatch(Object value) throws RestCallException {
return header("If-None-Match", value);
}
@@ -713,8 +774,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall ifRange(Object value) {
+ public RestCall ifRange(Object value) throws RestCallException {
return header("If-Range", value);
}
@@ -725,8 +787,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall ifUnmodifiedSince(Object value) {
+ public RestCall ifUnmodifiedSince(Object value) throws RestCallException {
return header("If-Unmodified-Since", value);
}
@@ -737,8 +800,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall maxForwards(Object value) {
+ public RestCall maxForwards(Object value) throws RestCallException {
return header("Max-Forwards", value);
}
@@ -749,8 +813,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall origin(Object value) {
+ public RestCall origin(Object value) throws RestCallException {
return header("Origin", value);
}
@@ -761,8 +826,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall pragma(Object value) {
+ public RestCall pragma(Object value) throws RestCallException {
return header("Pragma", value);
}
@@ -773,8 +839,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall proxyAuthorization(Object value) {
+ public RestCall proxyAuthorization(Object value) throws RestCallException {
return header("Proxy-Authorization", value);
}
@@ -785,8 +852,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall range(Object value) {
+ public RestCall range(Object value) throws RestCallException {
return header("Range", value);
}
@@ -797,8 +865,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall referer(Object value) {
+ public RestCall referer(Object value) throws RestCallException {
return header("Referer", value);
}
@@ -809,8 +878,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall te(Object value) {
+ public RestCall te(Object value) throws RestCallException {
return header("TE", value);
}
@@ -821,8 +891,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall userAgent(Object value) {
+ public RestCall userAgent(Object value) throws RestCallException {
return header("User-Agent", value);
}
@@ -833,8 +904,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall upgrade(Object value) {
+ public RestCall upgrade(Object value) throws RestCallException {
return header("Upgrade", value);
}
@@ -845,8 +917,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall via(Object value) {
+ public RestCall via(Object value) throws RestCallException {
return header("Via", value);
}
@@ -857,8 +930,9 @@ public final class RestCall {
*
* @param value The new header value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall warning(Object value) {
+ public RestCall warning(Object value) throws RestCallException {
return header("Warning", value);
}
@@ -867,8 +941,9 @@ public final class RestCall {
*
* @param version The version string (e.g. <js>"1.2.3"</js>)
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall clientVersion(String version) {
+ public RestCall clientVersion(String version) throws RestCallException {
return header("X-Client-Version", version);
}
@@ -1827,9 +1902,18 @@ public final class RestCall {
*
* @param value The debug value.
* @return This object (for method chaining).
+ * @throws RestCallException
*/
- public RestCall debug(boolean value) {
+ public RestCall debug(boolean value) throws RestCallException {
header("Debug", value);
return this;
}
+
+ private boolean isBean(Object o) throws RestCallException {
+ return getBeanContext().isBean(o);
+ }
+
+ private BeanMap<?> toBeanMap(Object o) throws RestCallException {
+ return getBeanContext().createSession().toBeanMap(o);
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 e5f9a32..b026ff4 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
@@ -537,6 +537,9 @@ public class RestClient extends CoreObject {
RestCall rc = (httpMethod.equals("POST") ? doPost(url) : doGet(url));
rc.serializer(serializer).parser(parser);
+ for (RemoteMethodArg a : rmm.getPathArgs())
+ rc.path(a.name, args[a.index]);
+
for (RemoteMethodArg a : rmm.getQueryArgs())
rc.query(a.name, args[a.index], a.skipIfNE);
@@ -598,6 +601,8 @@ public class RestClient extends CoreObject {
s = sb.toString();
}
}
+ if (s.indexOf('{') != -1)
+ s = s.replace("{", "%7B").replace("}", "%7D");
return new URI(s);
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/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 1d583ce..d039d30 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
@@ -447,6 +447,55 @@ public class ThirdPartyProxyResource extends ResourceJena {
return "OK";
}
+ @RestMethod(name="GET", path="/stringQuery1")
+ public String stringQuery1(
+ @Query("a") int a,
+ @Query("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="GET", path="/stringQuery2")
+ public String stringQuery2(
+ @Query("a") int a,
+ @Query("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="GET", path="/mapQuery")
+ public String mapQuery(
+ @Query("a") int a,
+ @Query("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="GET", path="/beanQuery")
+ public String beanQuery(
+ @Query("a") int a,
+ @Query("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+
//--------------------------------------------------------------------------------
// FormData tests
//--------------------------------------------------------------------------------
@@ -654,6 +703,49 @@ public class ThirdPartyProxyResource extends ResourceJena {
return "OK";
}
+
+ //--------------------------------------------------------------------------------
+ // Path tests
+ //--------------------------------------------------------------------------------
+
+ @RestMethod(name="POST", path="/pathVars1/{a}/{b}")
+ public String pathVars1(
+ @Path("a") int a,
+ @Path("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+
+ @RestMethod(name="POST", path="/pathVars2/{a}/{b}")
+ public String pathVars2(
+ @Path("a") int a,
+ @Path("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+ @RestMethod(name="POST", path="/pathVars3/{a}/{b}")
+ public String pathVars3(
+ @Path("a") int a,
+ @Path("b") String b
+ ) throws Exception {
+
+ assertEquals(1, a);
+ assertEquals("foo", b);
+
+ return "OK";
+ }
+
+
//--------------------------------------------------------------------------------
// Test return types.
//--------------------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/3c1ff80f/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
index 2f69bb1..7b73358 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
@@ -300,6 +300,34 @@ public class ThirdPartyProxyTest extends RestTestcase {
assertEquals("OK", r);
}
+ @Test
+ public void b08_stringQuery1() throws Exception {
+ String r = proxy.stringQuery1("a=1&b=foo");
+ assertEquals("OK", r);
+ }
+
+ @Test
+ public void b09_stringQuery2() throws Exception {
+ String r = proxy.stringQuery2("a=1&b=foo");
+ assertEquals("OK", r);
+ }
+
+ @Test
+ public void b10_mapQuery() throws Exception {
+ String r = proxy.mapQuery(
+ new AMap<String,Object>().append("a", 1).append("b", "foo")
+ );
+ assertEquals("OK", r);
+ }
+
+ @Test
+ public void b11_beanQuery() throws Exception {
+ String r = proxy.beanQuery(
+ new ABean().init()
+ );
+ assertEquals("OK", r);
+ }
+
//--------------------------------------------------------------------------------
// FormData tests
//--------------------------------------------------------------------------------
@@ -1050,6 +1078,30 @@ public class ThirdPartyProxyTest extends RestTestcase {
proxy.setEnum1d3dListMap(new AMap<TestEnum,List<TestEnum[][][]>>().append(TestEnum.ONE, new AList<TestEnum[][][]>().append(new TestEnum[][][]{{{TestEnum.TWO,null},null},null}).append(null)));
}
+ // Path variables
+
+ @Test
+ public void f01_pathVars1() {
+ String r = proxy.pathVars1(1, "foo");
+ assertEquals("OK", r);
+ }
+
+ @Test
+ public void f02_pathVars2() {
+ String r = proxy.pathVars2(
+ new AMap<String,Object>().append("a", 1).append("b", "foo")
+ );
+ assertEquals("OK", r);
+ }
+
+ @Test
+ public void f03_pathVars3() {
+ String r = proxy.pathVars3(
+ new ABean().init()
+ );
+ assertEquals("OK", r);
+ }
+
//--------------------------------------------------------------------------------
// Proxy class
@@ -1224,6 +1276,27 @@ public class ThirdPartyProxyTest extends RestTestcase {
@Query("h8") Map<TestEnum,List<TestEnum[][][]>> h8
);
+ @RemoteMethod(httpMethod="GET", path="/stringQuery1")
+ String stringQuery1(
+ @Query() String q
+ );
+
+ @RemoteMethod(httpMethod="GET", path="/stringQuery2")
+ String stringQuery2(
+ @Query("*") String q
+ );
+
+ @RemoteMethod(httpMethod="GET", path="/mapQuery")
+ String mapQuery(
+ @Query("*") Map<String,Object> q
+ );
+
+ @RemoteMethod(httpMethod="GET", path="/beanQuery")
+ String beanQuery(
+ @Query("*") ABean q
+ );
+
+
//--------------------------------------------------------------------------------
// FormData tests
//--------------------------------------------------------------------------------
@@ -1308,6 +1381,26 @@ public class ThirdPartyProxyTest extends RestTestcase {
);
//--------------------------------------------------------------------------------
+ // Path tests
+ //--------------------------------------------------------------------------------
+
+ @RemoteMethod(httpMethod="POST", path="/pathVars1/{a}/{b}")
+ String pathVars1(
+ @Path("a") int a,
+ @Path("b") String b
+ );
+
+ @RemoteMethod(httpMethod="POST", path="/pathVars2/{a}/{b}")
+ String pathVars2(
+ @Path Map<String,Object> p
+ );
+
+ @RemoteMethod(httpMethod="POST", path="/pathVars3/{a}/{b}")
+ String pathVars3(
+ @Path ABean p
+ );
+
+ //--------------------------------------------------------------------------------
// Test return types.
//--------------------------------------------------------------------------------