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 2018/07/20 13:21:48 UTC
[juneau] branch master updated: Support @RequestBean as @RestMethod
parameter.
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new a56d59d Support @RequestBean as @RestMethod parameter.
a56d59d is described below
commit a56d59ded6c27c964331290f98e9358f7606acd7
Author: JamesBognar <ja...@apache.org>
AuthorDate: Fri Jul 20 09:21:32 2018 -0400
Support @RequestBean as @RestMethod parameter.
---
juneau-core/juneau-marshall/TODO.txt | 13 +-
.../org/apache/juneau/http/annotation/Body.java | 1 -
.../apache/juneau/http/annotation/FormData.java | 3 +-
.../org/apache/juneau/http/annotation/Header.java | 1 -
.../org/apache/juneau/http/annotation/Path.java | 1 -
.../org/apache/juneau/http/annotation/Query.java | 1 -
.../annotation}/RequestBean.java | 22 ++-
.../apache/juneau/httppart/HttpPartSerializer.java | 1 -
.../apache/juneau/httppart/RequestBeanMeta.java | 188 +++++++++++++++++++++
.../juneau/httppart/RequestBeanPropertyMeta.java | 144 ++++++++++++++++
.../apache/juneau/internal/ReflectionUtils.java | 11 ++
.../apache/juneau/remoteable/RemoteMethodArg.java | 5 +-
.../juneau/remoteable/RemoteMethodBeanArg.java | 55 +-----
.../juneau/remoteable/RemoteableMethodMeta.java | 5 +-
juneau-doc/src/main/javadoc/overview.html | 53 +++++-
.../org/apache/juneau/rest/client/RestClient.java | 25 +--
.../java/org/apache/juneau/rest/RestContext.java | 4 +
.../org/apache/juneau/rest/RestParamDefaults.java | 14 ++
.../java/org/apache/juneau/rest/RestRequest.java | 58 +++++++
.../apache/juneau/rest/annotation/HookEvent.java | 2 +-
20 files changed, 515 insertions(+), 92 deletions(-)
diff --git a/juneau-core/juneau-marshall/TODO.txt b/juneau-core/juneau-marshall/TODO.txt
index 31d37b1..ea19c52 100644
--- a/juneau-core/juneau-marshall/TODO.txt
+++ b/juneau-core/juneau-marshall/TODO.txt
@@ -11,12 +11,7 @@
* specific language governing permissions and limitations under the License. *
***************************************************************************************************************************
-Document properties in config file.
-
-REST example showing how to use NLS.
-REST example showing how to customize look-and-feel.
-REST example showing static resources.
-Add doc link to @Example class.
-
-
-
+Test @Header("*") using maps/beans and related annotations.
+Simplify @Body annotation by merging @Schema into it.
+Test RestRequest.getRequestBean() and @RequestBean as an annotation.
+Document RestRequest.getRequestBean().
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Body.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Body.java
index f6ef27e..1d6c2c2 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Body.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Body.java
@@ -22,7 +22,6 @@ import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.json.*;
import org.apache.juneau.jsonschema.*;
-import org.apache.juneau.remoteable.*;
import org.apache.juneau.serializer.*;
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
index 78a736f..556c420 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/FormData.java
@@ -21,7 +21,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
-import org.apache.juneau.remoteable.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -109,7 +108,7 @@ import org.apache.juneau.urlencoding.*;
* </ul>
*
* <h5 class='topic'>Client-side REST</h5>
-* Annotation applied to Java method arguments of interface proxies to denote that they are FORM post parameters on the
+ * Annotation applied to Java method arguments of interface proxies to denote that they are FORM post parameters on the
* request.
*
* <h5 class='section'>Example:</h5>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
index 20fcb25..7b71cae 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Header.java
@@ -20,7 +20,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
-import org.apache.juneau.remoteable.*;
import org.apache.juneau.urlencoding.*;
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java
index 8317155..850246c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Path.java
@@ -19,7 +19,6 @@ import java.lang.annotation.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
-import org.apache.juneau.remoteable.*;
import org.apache.juneau.urlencoding.*;
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
index 0f06781..b06f5bf 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Query.java
@@ -21,7 +21,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
-import org.apache.juneau.remoteable.*;
import org.apache.juneau.urlencoding.*;
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RequestBean.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RequestBean.java
similarity index 85%
rename from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RequestBean.java
rename to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RequestBean.java
index 249b83d..c94b4ee 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RequestBean.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RequestBean.java
@@ -10,7 +10,7 @@
// * "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;
+package org.apache.juneau.http.annotation;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@@ -21,8 +21,21 @@ import org.apache.juneau.httppart.*;
import org.apache.juneau.urlencoding.*;
/**
- * Annotation applied to Java method arguments of interface proxies to denote a bean with remoteable annotations.
+ * Request bean annotation.
+ *
+ * <p>
+ * Can be used in the following locations:
+ * <ul>
+ * <li>Java method arguments and argument-types of client-side <ja>@Remoteable</ja>-annotated REST interface proxies.
+ * <li>Java method arguments and argument-types of server-side <ja>@RestMethod</ja>-annotated REST Java methods.
+ * </ul>
*
+ * <h5 class='topic'>Server-side REST</h5>
+ * TODO
+ *
+ * <h5 class='topic'>Client-side REST</h5>
+ * Annotation applied to Java method arguments of interface proxies to denote a bean with remoteable annotations.
+
* <h5 class='section'>Example:</h5>
* <p class='bcode w800'>
* <ja>@Remoteable</ja>(path=<js>"/myproxy"</js>)
@@ -103,4 +116,9 @@ public @interface RequestBean {
* This annotation is provided to allow values to be custom serialized.
*/
Class<? extends HttpPartSerializer> serializer() default HttpPartSerializer.Null.class;
+
+ /**
+ * TODO
+ */
+ Class<? extends HttpPartParser> parser() default HttpPartParser.Null.class;
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java
index 046db35..88062c5 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSerializer.java
@@ -14,7 +14,6 @@ package org.apache.juneau.httppart;
import org.apache.juneau.*;
import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.remoteable.*;
import org.apache.juneau.serializer.*;
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/RequestBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/RequestBeanMeta.java
new file mode 100644
index 0000000..04b369b
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/RequestBeanMeta.java
@@ -0,0 +1,188 @@
+// ***************************************************************************************************************************
+// * 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.httppart;
+
+import static org.apache.juneau.internal.ReflectionUtils.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Represents the metadata gathered from a parameter or class annotated with {@link RequestBean}.
+ */
+public class RequestBeanMeta {
+
+ /**
+ * Create metadata from specified parameter.
+ *
+ * @param m The method containing the parameter or parameter type annotated with {@link RequestBean}.
+ * @param i The parameter index.
+ * @param ps
+ * Configuration information used to instantiate part serializers and part parsers.
+ * <br>Can be <jk>null</jk>.
+ * @return Metadata about the parameter, or <jk>null</jk> if parameter or parameter type not annotated with {@link RequestBean}.
+ */
+ public static RequestBeanMeta create(Method m, int i, PropertyStore ps) {
+ if (! hasAnnotation(RequestBean.class, m, i))
+ return null;
+ return new RequestBeanMeta.Builder(ps).apply(m, i).build();
+ }
+
+ /**
+ * Create metadata from specified class.
+ *
+ * @param c The class annotated with {@link RequestBean}.
+ * @param ps
+ * Configuration information used to instantiate part serializers and part parsers.
+ * <br>Can be <jk>null</jk>.
+ * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link RequestBean}.
+ */
+ public static RequestBeanMeta create(Class<?> c, PropertyStore ps) {
+ if (! hasAnnotation(RequestBean.class, c))
+ return null;
+ return new RequestBeanMeta.Builder(ps).apply(c).build();
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Instance
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private final ClassMeta<?> cm;
+ private final Map<String,RequestBeanPropertyMeta> properties, propertiesByGetter;
+ private final HttpPartSerializer serializer;
+ private final HttpPartParser parser;
+
+ RequestBeanMeta(Builder b) {
+ this.cm = b.cm;
+ this.serializer = ClassUtils.newInstance(HttpPartSerializer.class, b.serializer, true, b.ps);
+ this.parser = ClassUtils.newInstance(HttpPartParser.class, b.parser, true, b.ps);
+ Map<String,RequestBeanPropertyMeta> properties = new LinkedHashMap<>(), propertiesByGetter = new LinkedHashMap<>();
+ for (Map.Entry<String,RequestBeanPropertyMeta.Builder> e : b.properties.entrySet()) {
+ RequestBeanPropertyMeta pm = e.getValue().build(serializer, parser);
+ properties.put(e.getKey(), pm);
+ propertiesByGetter.put(pm.getGetter(), pm);
+
+ }
+ this.properties = Collections.unmodifiableMap(properties);
+ this.propertiesByGetter = Collections.unmodifiableMap(propertiesByGetter);
+ }
+
+ static class Builder {
+ ClassMeta<?> cm;
+ PropertyStore ps;
+ Class<? extends HttpPartSerializer> serializer;
+ Class<? extends HttpPartParser> parser;
+ Map<String,RequestBeanPropertyMeta.Builder> properties = new LinkedHashMap<>();
+
+ Builder(PropertyStore ps) {
+ this.ps = ps;
+ }
+
+ Builder apply(Method m, int i) {
+ return apply(m.getParameterTypes()[i]).apply(getAnnotation(RequestBean.class, m, i));
+ }
+
+ Builder apply(Class<?> c) {
+ apply(getAnnotation(RequestBean.class, c));
+ this.cm = BeanContext.DEFAULT.getClassMeta(c);
+ BeanMeta<?> bm = BeanContext.DEFAULT.getBeanMeta(c);
+ for (BeanPropertyMeta bp : bm.getPropertyMetas()) {
+ String n = bp.getName();
+ Annotation a = bp.getAnnotation(Path.class);
+ String g = bp.getGetter().getName();
+ if (a != null) {
+ HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
+ getProperty(n, HttpPartType.PATH).apply(s).getter(g);
+ }
+ a = bp.getAnnotation(Header.class);
+ if (a != null) {
+ HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
+ getProperty(n, HttpPartType.HEADER).apply(s).getter(g);
+ }
+ a = bp.getAnnotation(Query.class);
+ if (a != null) {
+ HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
+ getProperty(n, HttpPartType.QUERY).apply(s).getter(g);
+ }
+ a = bp.getAnnotation(FormData.class);
+ if (a != null) {
+ HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
+ getProperty(n, HttpPartType.FORMDATA).apply(s).getter(g);
+ }
+ a = bp.getAnnotation(Body.class);
+ if (a != null) {
+ HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
+ getProperty(n, HttpPartType.BODY).apply(s).getter(g);
+ }
+ }
+ return this;
+ }
+
+ Builder apply(RequestBean rb) {
+ if (rb != null) {
+ if (rb.serializer() != HttpPartSerializer.Null.class)
+ serializer = rb.serializer();
+ if (rb.parser() != HttpPartParser.Null.class)
+ parser = rb.parser();
+ }
+ return this;
+ }
+
+ RequestBeanMeta build() {
+ return new RequestBeanMeta(this);
+ }
+
+ private RequestBeanPropertyMeta.Builder getProperty(String name, HttpPartType partType) {
+ RequestBeanPropertyMeta.Builder b = properties.get(name);
+ if (b == null) {
+ b = RequestBeanPropertyMeta.create().name(name).partType(partType);
+ properties.put(name, b);
+ }
+ return b;
+ }
+ }
+
+ /**
+ * Returns metadata about the bean property with the specified property name.
+ *
+ * @param name The bean property name.
+ * @return Metadata about the bean property, or <jk>null</jk> if none found.
+ */
+ public RequestBeanPropertyMeta getProperty(String name) {
+ return properties.get(name);
+ }
+
+ /**
+ * Returns metadata about the bean property with the specified method getter name.
+ *
+ * @param name The bean method getter name.
+ * @return Metadata about the bean property, or <jk>null</jk> if none found.
+ */
+ public RequestBeanPropertyMeta getPropertyByGetter(String name) {
+ return propertiesByGetter.get(name);
+ }
+
+ /**
+ * Returns metadata about the class.
+ *
+ * @return Metadata about the class.
+ */
+ public ClassMeta<?> getClassMeta() {
+ return cm;
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/RequestBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/RequestBeanPropertyMeta.java
new file mode 100644
index 0000000..1ab0426
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/RequestBeanPropertyMeta.java
@@ -0,0 +1,144 @@
+// ***************************************************************************************************************************
+// * 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.httppart;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Represents the metadata gathered from a getter method of a class annotated with {@link RequestBean}.
+ */
+public class RequestBeanPropertyMeta {
+
+ static RequestBeanPropertyMeta.Builder create() {
+ return new Builder();
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Instance
+ //-----------------------------------------------------------------------------------------------------------------
+
+ private final String partName, getter;
+ private final HttpPartType partType;
+ private final HttpPartSerializer serializer;
+ private final HttpPartParser parser;
+ private final HttpPartSchema schema;
+
+ RequestBeanPropertyMeta(Builder b, HttpPartSerializer serializer, HttpPartParser parser) {
+ this.partType = b.partType;
+ this.schema = b.schema.build();
+ this.partName = StringUtils.firstNonEmpty(schema.getName(), b.name);
+ this.getter = b.getter;
+ this.serializer = schema.getSerializer() == null ? serializer : ClassUtils.newInstance(HttpPartSerializer.class, schema.getSerializer(), true, b.ps);
+ this.parser = schema.getParser() == null ? parser : ClassUtils.newInstance(HttpPartParser.class, schema.getParser(), true, b.ps);
+ }
+
+ static class Builder {
+ HttpPartType partType;
+ HttpPartSchemaBuilder schema;
+ String name, getter;
+ PropertyStore ps = PropertyStore.DEFAULT;
+
+ Builder name(String value) {
+ name = value;
+ return this;
+ }
+
+ Builder getter(String value) {
+ getter = value;
+ return this;
+ }
+
+ Builder partType(HttpPartType value) {
+ partType = value;
+ return this;
+ }
+
+ Builder schema(HttpPartSchemaBuilder value) {
+ schema = value;
+ return this;
+ }
+
+ Builder apply(HttpPartSchemaBuilder s) {
+ schema = s;
+ return this;
+ }
+
+ RequestBeanPropertyMeta build(HttpPartSerializer serializer, HttpPartParser parser) {
+ return new RequestBeanPropertyMeta(this, serializer, parser);
+ }
+ }
+
+ /**
+ * Returns the HTTP part name for this property (e.g. query parameter name).
+ *
+ * @return The HTTP part name, or <jk>null</jk> if it doesn't have a part name.
+ */
+ public String getPartName() {
+ return partName;
+ }
+
+ /**
+ * Returns the name of the Java method getter that defines this property.
+ *
+ * @return
+ * The name of the Java method getter that defines this property.
+ * <br>Never <jk>null</jk>.
+ */
+ public String getGetter() {
+ return getter;
+ }
+
+ /**
+ * Returns the HTTP part type for this property (e.g. query parameter, header, etc...).
+ *
+ * @return
+ * The HTTP part type for this property.
+ * <br>Never <jk>null</jk>.
+ */
+ public HttpPartType getPartType() {
+ return partType;
+ }
+
+ /**
+ * Returns the serializer to use for serializing the bean property value.
+ *
+ * @param _default The default serializer to use if not defined on the annotation.
+ * @return The serializer to use for serializing the bean property value.
+ */
+ public HttpPartSerializer getSerializer(HttpPartSerializer _default) {
+ return serializer == null ? _default : serializer;
+ }
+
+ /**
+ * Returns the parser to use for parsing the bean property value.
+ *
+ * @param _default The default parsing to use if not defined on the annotation.
+ * @return The parsing to use for serializing the bean property value.
+ */
+ public HttpPartParser getParser(HttpPartParser _default) {
+ return parser == null ? _default : parser;
+ }
+
+ /**
+ * Returns the schema information gathered from annotations on the method and return type.
+ *
+ * @return
+ * The schema information gathered from annotations on the method and return type.
+ * <br>Never <jk>null</jk>.
+ */
+ public HttpPartSchema getSchema() {
+ return schema;
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReflectionUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
index 0464c3f..213fb54 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
@@ -48,6 +48,17 @@ public final class ReflectionUtils {
}
/**
+ * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Class)} returns a value.
+ *
+ * @param a The annotation to check for.
+ * @param c The class to check.
+ * @return <jk>true</jk> if the {@link #getAnnotation(Class, Class)} returns a value.
+ */
+ public static boolean hasAnnotation(Class<? extends Annotation> a, Class<?> c) {
+ return getAnnotation(a, c) != null;
+ }
+
+ /**
* Returns the specified annotation if it exists on the specified parameter or parameter type class.
*
* @param a The annotation to check for.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
index 5725950..ae08a68 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
@@ -96,10 +96,11 @@ public final class RemoteMethodArg {
/**
* Returns the HTTP part serializer to use for serializing this part.
*
+ * @param _default The default serializer to use if the serializer was not defined via annotations.
* @return The HTTP part serializer, or <jk>null</jk> if not specified.
*/
- public HttpPartSerializer getSerializer() {
- return serializer;
+ public HttpPartSerializer getSerializer(HttpPartSerializer _default) {
+ return serializer == null ? _default : serializer;
}
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java
index c682a29..99b5bb0 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java
@@ -13,14 +13,7 @@
package org.apache.juneau.remoteable;
import static org.apache.juneau.internal.ClassUtils.*;
-import static org.apache.juneau.internal.ReflectionUtils.*;
-import static org.apache.juneau.httppart.HttpPartType.*;
-import java.lang.annotation.*;
-import java.lang.reflect.*;
-import java.util.*;
-
-import org.apache.juneau.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.httppart.*;
@@ -35,13 +28,13 @@ import org.apache.juneau.httppart.*;
public final class RemoteMethodBeanArg {
private final int index;
+ private final RequestBeanMeta meta;
private final HttpPartSerializer serializer;
- private final Map<String,RemoteMethodArg> properties;
- private RemoteMethodBeanArg(int index, Class<? extends HttpPartSerializer> serializer, Map<String,RemoteMethodArg> properties) {
+ RemoteMethodBeanArg(int index, Class<? extends HttpPartSerializer> serializer, RequestBeanMeta meta) {
this.index = index;
this.serializer = newInstance(HttpPartSerializer.class, serializer);
- this.properties = properties;
+ this.meta = meta;
}
/**
@@ -68,45 +61,7 @@ public final class RemoteMethodBeanArg {
* @param name The bean property name.
* @return Metadata about the bean property, or <jk>null</jk> if not found.
*/
- public RemoteMethodArg getProperty(String name) {
- return properties.get(name);
- }
-
- static RemoteMethodBeanArg create(Method m, int i) {
- Map<String,RemoteMethodArg> map = new LinkedHashMap<>();
- if (hasAnnotation(RequestBean.class, m, i)) {
- RequestBean rb = getAnnotation(RequestBean.class, m, i);
- BeanMeta<?> bm = BeanContext.DEFAULT.getBeanMeta(m.getParameterTypes()[i]);
- for (BeanPropertyMeta bp : bm.getPropertyMetas()) {
- String n = bp.getName();
- Annotation a = bp.getAnnotation(Path.class);
- if (a != null) {
- HttpPartSchema s = HttpPartSchema.create().apply(a).build();
- map.put(n, new RemoteMethodArg(PATH, s, n));
- }
- a = bp.getAnnotation(Header.class);
- if (a != null) {
- HttpPartSchema s = HttpPartSchema.create().apply(a).build();
- map.put(n, new RemoteMethodArg(HEADER, s, n));
- }
- a = bp.getAnnotation(Query.class);
- if (a != null) {
- HttpPartSchema s = HttpPartSchema.create().apply(a).build();
- map.put(n, new RemoteMethodArg(QUERY, s, n));
- }
- a = bp.getAnnotation(FormData.class);
- if (a != null) {
- HttpPartSchema s = HttpPartSchema.create().apply(a).build();
- map.put(n, new RemoteMethodArg(FORMDATA, s, n));
- }
- a = bp.getAnnotation(Body.class);
- if (a != null) {
- HttpPartSchema s = HttpPartSchema.create().apply(a).build();
- map.put(n, new RemoteMethodArg(BODY, s, n));
- }
- }
- return new RemoteMethodBeanArg(i, rb.serializer(), map);
- }
- return null;
+ public RequestBeanPropertyMeta getProperty(String name) {
+ return meta.getProperty(name);
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
index ca3a71a..bc4a08b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
@@ -19,6 +19,7 @@ import static org.apache.juneau.httppart.HttpPartType.*;
import java.lang.reflect.*;
import java.util.*;
+import org.apache.juneau.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.httppart.*;
@@ -119,10 +120,10 @@ public class RemoteableMethodMeta {
else
annotated = false;
}
- RemoteMethodBeanArg rmba = RemoteMethodBeanArg.create(m, i);
+ RequestBeanMeta rmba = RequestBeanMeta.create(m, i, PropertyStore.DEFAULT);
if (rmba != null) {
annotated = true;
- requestBeanArgs.add(rmba);
+ requestBeanArgs.add(new RemoteMethodBeanArg(i, null, rmba));
}
if (! annotated) {
otherArgs.add(new RemoteMethodArg(i, BODY, null));
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index bbb8127..194f67d 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -309,6 +309,7 @@
<li><p><a class='doclink' href='#juneau-rest-server.HasQuery'>@HasQuery</a></p>
<li><p><a class='doclink' href='#juneau-rest-server.Header'>@Header</a></p>
<li><p><a class='doclink' href='#juneau-rest-server.Path'>@Path</a></p>
+ <li><p><a class='doclink' href='#juneau-rest-server.ResponseBean'>@ResponseBean</a></p>
<li><p><a class='doclink' href='#juneau-rest-server.SwaggerSchemaPartParsing'>Swagger Schema Part Parsing</a></p>
<li><p><a class='doclink' href='#juneau-rest-server.Response'>@Response</a></p>
<li><p><a class='doclink' href='#juneau-rest-server.Responses'>@Responses</a></p>
@@ -13218,10 +13219,11 @@
</ul>
</div>
- <!-- === 7.14 - @PathRemainer ======================================================================= -->
+ <!-- === 7.14 - @ResponseBean ======================================================================= -->
- <h3 class='topic' onclick='toggle(this)'><a href='#juneau-rest-server.PathRemainer' id='juneau-rest-server.PathRemainer'>7.14 - @PathRemainer</a></h3>
+ <h3 class='topic' onclick='toggle(this)'><a href='#juneau-rest-server.PathRemainer' id='juneau-rest-server.ResponseBean'>7.14 - @ResponseBean</a></h3>
<div class='topic'>
+ TODO
</div>
<!-- === 7.15 - Swagger Schema Part Parsing ========================================================= -->
@@ -22722,6 +22724,38 @@
<h5 class='topic w800'>juneau-marshall</h5>
<ul class='spaced-list'>
<li>
+ The REST client <ja>@Remoteable</ja> annotations and REST server <ja>@RemoteMethod</ja> annotations which used to be
+ in separate packages in the client and server projects have been combined into a single set of annotations in
+ the {@link org.apache.juneau.http.annotation} package.
+ <br>This fixes a long-standing problem where it was easy to mix up using client-side annotations in server-side code, and vis-versa.
+ <br>Additionally, much work has been done on these annotations to add support for Swagger-style validations and documentation.
+ <ul class='doctree'>
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Body}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.FormData}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Header}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Path}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Query}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.HasFormData}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.HasQuery}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.RequestBean}
+ </ul>
+ <br>These are used with new Swagger schema/documentation annotations to produce schema-based serialization/parsing/validation
+ and auto-generated Swagger documentation:
+ <ul class='doctree'>
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Contact}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.ExternalDocs}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Items}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.License}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Schema}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.SubItems}
+ <li class='ja'>{@link org.apache.juneau.http.annotation.Tag}
+ </ul>
+ <li>
+ Support for multi-valued parameters as maps or beans on server-side annotations (it was previously supported on client-side):
+ <code><ja>@Query</ja>(<js>"*"</js>)</code>, <code><ja>@FormData</ja>(<js>"*"</js>)</code>, <code><ja>@Header</ja>(<js>"*"</js>)</code>, <code><ja>@Path</ja>(<js>"*"</js>)</code>
+ <li>
+ Support for server-side use of <ja>@ResourceBean</ja> annotation on <ja>@RestMethod</ja> annotations and new {@link org.apache.juneau.rest.RestRequest#getRequestBean(RequestBeanMeta)} method.
+ <li>
Fixed bug where <code><ja>@Bean</ja>(typeName)</code> was not being detected on non-bean POJO classes.
<li>
Fixed bug where HTML-Schema was not being rendered correctly.
@@ -22840,12 +22874,6 @@
</ul>
</ul>
<li>
- The REST client <ja>@Remoteable</ja> annotations and REST server <ja>@RemoteMethod</ja> annotations which used to be
- in separate packages in the client and server projects have been combined into a single set of annotations in
- the {@link org.apache.juneau.http.annotation} package.
- <br>This fixes a long-standing problem where it was easy to mix up using client-side annotations in server-side code, and vis-versa.
- <br>Additionally, much work has been done on these annotations to add support for Swagger-style validations and documentation.
- <li>
The <code>JsonSerializer.Simple</code> class has been moved into the top-level {@link org.apache.juneau.json.SimpleJsonSerializer} class.
<li>
RDF serializer subclasses have been moved into top-level classes:
@@ -23011,6 +23039,15 @@
<li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#pojoSwaps()}
</ul>
<br>One advantage is that you now have control over the precedence of serializers and parsers by where you insert the <code>Inherit</code> class.
+ <li>
+ <code>RequestPathMatch</code> class has been renamed to {@link org.apache.juneau.rest.RequestPath}.
+ <li>
+ {@link org.apache.juneau.http.annotation.RequestBean @RequestBean} objects can now be used as parameters in <ja>@RestMethod</ja> methods.
+ <br>Includes new methods on {@link org.apache.juneau.rest.RestRequest}:
+ <ul class='doctree'>
+ <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getRequestBean(Class) getRequestBean(Class)}
+ <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getRequestBean(RequestBeanMeta) getRequestBean(RequestBeanMeta)}
+ </ul>
</ul>
<h5 class='topic w800'>juneau-rest-client</h5>
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index a5e34f0..d9f06c6 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -1034,25 +1034,27 @@ public class RestClient extends BeanContext implements Closeable {
String url = rmm.getUrl();
String httpMethod = rmm.getHttpMethod();
+ HttpPartSerializer s = getPartSerializer();
+
try (RestCall rc = doCall(httpMethod, url, httpMethod.equals("POST") || httpMethod.equals("PUT"))) {
rc.serializer(serializer).parser(parser);
for (RemoteMethodArg a : rmm.getPathArgs())
- rc.path(a.getName(), args[a.getIndex()], a.getSerializer(), a.getSchema());
+ rc.path(a.getName(), args[a.getIndex()], a.getSerializer(s), a.getSchema());
for (RemoteMethodArg a : rmm.getQueryArgs())
- rc.query(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(), a.getSchema());
+ rc.query(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(s), a.getSchema());
for (RemoteMethodArg a : rmm.getFormDataArgs())
- rc.formData(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(), a.getSchema());
+ rc.formData(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(s), a.getSchema());
for (RemoteMethodArg a : rmm.getHeaderArgs())
- rc.header(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(), a.getSchema());
+ rc.header(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(s), a.getSchema());
RemoteMethodArg ba = rmm.getBodyArg();
if (ba != null)
- rc.body(args[ba.getIndex()], ba.getSerializer(), ba.getSchema());
+ rc.body(args[ba.getIndex()], ba.getSerializer(null), ba.getSchema());
if (rmm.getRequestBeanArgs().length > 0) {
BeanSession bs = createBeanSession();
@@ -1062,20 +1064,21 @@ public class RestClient extends BeanContext implements Closeable {
for (BeanPropertyValue bpv : bm.getValues(false)) {
BeanPropertyMeta pMeta = bpv.getMeta();
Object val = bpv.getValue();
- RemoteMethodArg a = rmba.getProperty(pMeta.getName());
+ RequestBeanPropertyMeta a = rmba.getProperty(pMeta.getName());
if (a != null) {
HttpPartType pt = a.getPartType();
+ HttpPartSchema schema = a.getSchema();
if (pt == PATH)
- rc.path(a.getName(), val, ObjectUtils.firstNonNull(a.getSerializer(), rmba.getSerializer()), a.getSchema());
+ rc.path(a.getPartName(), val, a.getSerializer(s), schema);
if (val != null) {
if (pt == QUERY) {
- rc.query(a.getName(), val, a.isSkipIfEmpty(), ObjectUtils.firstNonNull(a.getSerializer(), rmba.getSerializer()), a.getSchema());
+ rc.query(a.getPartName(), val, schema.isSkipIfEmpty(), a.getSerializer(s), schema);
} else if (pt == FORMDATA) {
- rc.formData(a.getName(), val, a.isSkipIfEmpty(), ObjectUtils.firstNonNull(a.getSerializer(), rmba.getSerializer()), a.getSchema());
+ rc.formData(a.getPartName(), val, schema.isSkipIfEmpty(), a.getSerializer(s), schema);
} else if (pt == HEADER) {
- rc.header(a.getName(), val, a.isSkipIfEmpty(), ObjectUtils.firstNonNull(a.getSerializer(), rmba.getSerializer()), a.getSchema());
+ rc.header(a.getPartName(), val, schema.isSkipIfEmpty(), a.getSerializer(s), schema);
} else if (pt == HttpPartType.BODY) {
- rc.body(val, ObjectUtils.firstNonNull(a.getSerializer(), rmba.getSerializer()), a.getSchema());
+ rc.body(val, a.getSerializer(s), schema);
}
}
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index e0df82e..20ddb05 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -4349,6 +4349,10 @@ public final class RestContext extends BeanContext {
s = HttpPartSchema.create(Body.class, method, i);
rp[i] = new RestParamDefaults.BodyObject(method, s, t, ps);
+ } else if (hasAnnotation(RequestBean.class, method, i)) {
+ RequestBeanMeta rbm = RequestBeanMeta.create(method, i, ps);
+ rp[i] = new RestParamDefaults.RequestBeanObject(method, rbm, t);
+
} else if (hasAnnotation(Response.class, method, i)) {
s = HttpPartSchema.create(Response.class, method, i);
rp[i] = new RestParamDefaults.ResponseObject(method, s, t);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index 5b85476..a1635e0 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -601,6 +601,20 @@ class RestParamDefaults {
}
}
+ static final class RequestBeanObject extends RestMethodParam {
+ private final RequestBeanMeta requestBeanMeta;
+
+ protected RequestBeanObject(Method m, RequestBeanMeta rbm, Type t) {
+ super(OTHER, m, null, t, null);
+ this.requestBeanMeta = rbm;
+ }
+
+ @Override /* RestMethodParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getRequestBean(requestBeanMeta);
+ }
+ }
+
static final class ResponseHeaderObject extends RestMethodParam {
final HttpPartSerializer partSerializer;
final HttpPartSchema schema;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 6fc9d0d..3e2dfd9 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -15,11 +15,15 @@ package org.apache.juneau.rest;
import static java.util.Collections.*;
import static java.util.logging.Level.*;
import static org.apache.juneau.html.HtmlDocSerializer.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
import static org.apache.juneau.internal.IOUtils.*;
+import static org.apache.juneau.remoteable.ReturnValue.*;
import static org.apache.juneau.serializer.Serializer.*;
import java.io.*;
+import java.lang.reflect.*;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.net.*;
import java.nio.charset.*;
import java.text.*;
@@ -37,6 +41,7 @@ import org.apache.juneau.http.annotation.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
+import org.apache.juneau.remoteable.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.exception.*;
import org.apache.juneau.rest.helper.*;
@@ -1403,6 +1408,59 @@ public final class RestRequest extends HttpServletRequestWrapper {
return restJavaMethod.widgets;
}
+ /**
+ * TODO
+ *
+ * @param c The request bean interface to instantiate.
+ * @return A new request bean proxy for this REST request.
+ */
+ public <T> T getRequestBean(Class<T> c) {
+ return getRequestBean(RequestBeanMeta.create(c, getContext().getPropertyStore()));
+ }
+
+ /**
+ * Same as {@link #getRequestBean(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects.
+ *
+ * @param requestBeanMeta The metadata about the request bean interface to create.
+ * @return A new request bean proxy for this REST request.
+ */
+ public <T> T getRequestBean(final RequestBeanMeta requestBeanMeta) {
+ try {
+ Class<T> c = (Class<T>)requestBeanMeta.getClassMeta().getInnerClass();
+ final BeanMeta<T> bm = getBeanSession().getBeanMeta(c);
+ return (T)Proxy.newProxyInstance(
+ c.getClassLoader(),
+ new Class[] { c },
+ new InvocationHandler() {
+ @Override /* InvocationHandler */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ RequestBeanPropertyMeta pm = requestBeanMeta.getPropertyByGetter(method.getName());
+ if (pm != null) {
+ HttpPartParser pp = pm.getParser(getPartParser());
+ HttpPartSchema schema = pm.getSchema();
+ String name = pm.getPartName();
+ ClassMeta<?> type = getContext().getBeanContext().getClassMeta(method.getGenericReturnType());
+ HttpPartType pt = pm.getPartType();
+ if (pt == HttpPartType.BODY)
+ return getBody().asType(pm.getParser(null), schema, type);
+ if (pt == HttpPartType.QUERY)
+ return getQuery().get(pp, schema, name, type);
+ if (pt == HttpPartType.FORMDATA)
+ return getFormData().get(pp, schema, name, type);
+ if (pt == HttpPartType.HEADER)
+ return getHeaders().get(pp, schema, name, type);
+ if (pt == HttpPartType.PATH)
+ return getPathMatch().get(pp, schema, name, type);
+ }
+ return null;
+ }
+
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Override /* Object */
public String toString() {
StringBuilder sb = new StringBuilder("\n").append(getDescription()).append("\n");
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
index 56249bb..00808bf 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
@@ -164,7 +164,7 @@ public enum HookEvent {
* <li>{@link org.apache.juneau.parser.Parser}
* <li>{@link Locale}
* <li>{@link Swagger}
- * <li>{@link RequestPathMatch}
+ * <li>{@link RequestPath}
* <li>{@link RequestBody}
* <li>{@link Config}
* <li>{@link UriContext}