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 b45935d Merge functionality of @Response and @ResponseBody b45935d is described below commit b45935d79899cc4f46d294dcf5665eadcd4eab69 Author: JamesBognar <jamesbog...@apache.org> AuthorDate: Sun Aug 5 14:26:39 2018 -0400 Merge functionality of @Response and @ResponseBody --- .../http/annotation/AnnotationUtilsTest.java | 18 - .../juneau/http/annotation/AnnotationUtils.java | 28 -- .../apache/juneau/http/annotation/Response.java | 2 +- .../juneau/http/annotation/ResponseBody.java | 237 +----------- .../org/apache/juneau/httppart/HttpPartSchema.java | 2 - .../juneau/httppart/HttpPartSchemaBuilder.java | 9 - .../juneau/httppart/bean/ResponseBeanMeta.java | 109 ++++-- .../org/apache/juneau/internal/ClassUtils.java | 12 +- .../java/org/apache/juneau/rest/RestContext.java | 2 - .../org/apache/juneau/rest/RestJavaMethod.java | 12 +- .../org/apache/juneau/rest/RestParamDefaults.java | 32 +- .../java/org/apache/juneau/rest/RestResponse.java | 25 +- .../org/apache/juneau/rest/SwaggerGenerator.java | 38 -- .../juneau/rest/reshandlers/DefaultHandler.java | 29 +- .../rest/annotation/ResponseAnnotationTest.java | 409 +++++++++++++++++++- .../annotation/ResponseBodyAnnotationTest.java | 428 --------------------- 16 files changed, 496 insertions(+), 896 deletions(-) diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/http/annotation/AnnotationUtilsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/http/annotation/AnnotationUtilsTest.java index f9a9937..684e09a 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/http/annotation/AnnotationUtilsTest.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/http/annotation/AnnotationUtilsTest.java @@ -44,7 +44,6 @@ public class AnnotationUtilsTest { @Body @Response - @ResponseBody @ResponseHeader @X public static class A01 { @@ -56,7 +55,6 @@ public class AnnotationUtilsTest { public void testEmpty() throws Exception { assertTrue(empty(A01.class.getAnnotation(Body.class))); assertTrue(empty(A01.class.getAnnotation(Response.class))); - assertTrue(empty(A01.class.getAnnotation(ResponseBody.class))); assertTrue(empty(A01.class.getAnnotation(ResponseHeader.class))); X x = A01.class.getAnnotation(X.class); @@ -82,7 +80,6 @@ public class AnnotationUtilsTest { public void testEmptyNonExistent() throws Exception { assertTrue(empty(B01.class.getAnnotation(Body.class))); assertTrue(empty(B01.class.getAnnotation(Response.class))); - assertTrue(empty(B01.class.getAnnotation(ResponseBody.class))); assertTrue(empty(B01.class.getAnnotation(ResponseHeader.class))); assertTrue(empty((Contact)null)); @@ -171,21 +168,6 @@ public class AnnotationUtilsTest { assertTrue(usePartSerializer(D01e.class.getAnnotation(Body.class))); } - @ResponseBody public static class E01a {} - @ResponseBody(usePartSerializer=true) public static class E01b {} - @ResponseBody(schema=@Schema) public static class E01c {} - @ResponseBody(schema=@Schema(description="foo")) public static class E01d {} - @ResponseBody(partSerializer=OpenApiPartSerializer.class) public static class E01e {} - - @Test - public void usePartSerializerResponseBody() { - assertFalse(usePartSerializer(E01a.class.getAnnotation(ResponseBody.class))); - assertTrue(usePartSerializer(E01b.class.getAnnotation(ResponseBody.class))); - assertFalse(usePartSerializer(E01c.class.getAnnotation(ResponseBody.class))); - assertTrue(usePartSerializer(E01d.class.getAnnotation(ResponseBody.class))); - assertTrue(usePartSerializer(E01e.class.getAnnotation(ResponseBody.class))); - } - @Response public static class F01a {} @Response(usePartSerializer=true) public static class F01b {} @Response(schema=@Schema) public static class F01c {} diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/AnnotationUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/AnnotationUtils.java index f22c67a..8fdfcfc 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/AnnotationUtils.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/AnnotationUtils.java @@ -155,21 +155,6 @@ public class AnnotationUtils { && empty(a.schema()); } - - /** - * Returns <jk>true</jk> if the specified annotation contains all default values. - * - * @param a The annotation to check. - * @return <jk>true</jk> if the specified annotation contains all default values. - */ - public static boolean empty(ResponseBody a) { - if (a == null) - return true; - return - allEmpty(a.example(), a.examples(), a.api()) - && empty(a.schema()); - } - /** * Returns <jk>true</jk> if the specified annotation contains all default values. * @@ -333,19 +318,6 @@ public class AnnotationUtils { * @param a The annotation to check. * @return <jk>true</jk> if the part serializer should be used on the specified part. */ - public static boolean usePartSerializer(ResponseBody a) { - return - a.usePartSerializer() - || a.partSerializer() != HttpPartSerializer.Null.class - || ! empty(a.schema()); - } - - /** - * Returns <jk>true</jk> if the part serializer should be used on the specified part. - * - * @param a The annotation to check. - * @return <jk>true</jk> if the part serializer should be used on the specified part. - */ public static boolean usePartSerializer(Response a) { return a.usePartSerializer() diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java index 1ba720d..0c16949 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java @@ -48,7 +48,7 @@ import org.apache.juneau.jsonschema.*; * </ul> */ @Documented -@Target({PARAMETER,TYPE}) +@Target({PARAMETER,TYPE,METHOD}) @Retention(RUNTIME) @Inherited public @interface Response { diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseBody.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseBody.java index e1aafcb..cce8cf0 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseBody.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseBody.java @@ -17,244 +17,11 @@ import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.*; -import org.apache.juneau.*; -import org.apache.juneau.annotation.*; -import org.apache.juneau.httppart.*; -import org.apache.juneau.json.*; -import org.apache.juneau.jsonschema.*; - /** * TODO */ @Documented -@Target({PARAMETER,METHOD,TYPE}) +@Target(METHOD) @Retention(RUNTIME) @Inherited -public @interface ResponseBody { - - /** - * Specifies the {@link HttpPartSerializer} class used for serializing values to strings. - * - * <p> - * Overrides for this part the part serializer defined on the REST resource which by default is {@link OpenApiPartSerializer}. - */ - Class<? extends HttpPartSerializer> partSerializer() default HttpPartSerializer.Null.class; - - /** - * Specifies whether a part serializer should be used for serializing this value. - * - * <p> - * If <jk>false</jk>, then it indicates that normal Juneau serializers (e.g. {@link JsonSerializer}) should be used for this part. - */ - public boolean usePartSerializer() default false; - - /** - * <mk>schema</mk> field of the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#responseObject">Response</a> object. - * - * <h5 class='section'>Used for:</h5> - * <ul class='spaced-list'> - * <li> - * Server-side schema-based serializing and serializing validation. - * <li> - * Server-side generated Swagger documentation. - * </ul> - */ - Schema schema() default @Schema; - - /** - * A serialized example of the body of a response. - * - * <p> - * This is the <a class='doclink' href='../../../../../overview-summary.html#juneau-marshall.JsonDetails.SimplifiedJson'>Simplified JSON</a> of an example of the body. - * - * <p> - * This value is converted to a POJO and then serialized to all the registered serializers on the REST method to produce examples for all - * supported language types. - * <br>These values are then used to automatically populate the {@link #examples} field. - * - * <h5 class='section'>Example:</h5> - * <p class='bcode w800'> - * <jc>// A JSON representation of a PetCreate object.</jc> - * <ja>@ResponseBody</ja>( - * example=<js>"{name:'Doggie',price:9.99,species:'Dog',tags:['friendly','cute']}"</js> - * ) - * </p> - * - * <p> - * There are several other options for defining this example: - * <ul class='spaced-list'> - * <li> - * Defining an <js>"x-example"</js> field in the inherited Swagger JSON response object (classpath file or <code><ja>@ResourceSwagger</ja>(value)</code>/<code><ja>@MethodSwagger</ja>(value)</code>). - * <li> - * Defining an <js>"x-example"</js> field in the Swagger Schema Object for the response object (including referenced <js>"$ref"</js> schemas). - * <li> - * Allowing Juneau to auto-generate a code example. - * </ul> - * - * <p> - * The latter is important because Juneau also supports auto-generation of JSON-Schema from POJO classes using {@link JsonSchemaSerializer} which has several of it's own - * options for auto-detecting and calculation POJO examples. - * - * <p> - * In particular, examples can be defined via static methods, fields, and annotations on the classes themselves. - * - * <p class='bcode w800'> - * <jc>// Annotation on class.</jc> - * <ja>@Example</ja>(<js>"{name:'Doggie',price:9.99,species:'Dog',tags:['friendly','cute']}"</js>) - * <jk>public class</jk> PetCreate { - * ... - * } - * </p> - * <p class='bcode w800'> - * <jc>// Annotation on static method.</jc> - * <jk>public class</jk> PetCreate { - * - * <ja>@Example</ja> - * <jk>public static</jk> PetCreate <jsm>sample</jsm>() { - * <jk>return new</jk> PetCreate(<js>"Doggie"</js>, 9.99f, <js>"Dog"</js>, <jk>new</jk> String[] {<js>"friendly"</js>,<js>"cute"</js>}); - * } - * } - * </p> - * <p class='bcode w800'> - * <jc>// Static method with specific name 'example'.</jc> - * <jk>public class</jk> PetCreate { - * - * <jk>public static</jk> PetCreate <jsm>example</jsm>() { - * <jk>return new</jk> PetCreate(<js>"Doggie"</js>, 9.99f, <js>"Dog"</js>, <jk>new</jk> String[] {<js>"friendly"</js>,<js>"cute"</js>}); - * } - * } - * </p> - * <p class='bcode w800'> - * <jc>// Static field.</jc> - * <jk>public class</jk> PetCreate { - * - * <ja>@Example</ja> - * <jk>public static</jk> PetCreate <jsf>EXAMPLE</jsf> = <jk>new</jk> PetCreate(<js>"Doggie"</js>, 9.99f, <js>"Dog"</js>, <jk>new</jk> String[] {<js>"friendly"</js>,<js>"cute"</js>}); - * } - * </p> - * - * <p> - * Examples can also be specified via generic properties as well using the {@link BeanContext#BEAN_examples} property at either the class or method level. - * <p class='bcode w800'> - * <jc>// Examples defined at class level.</jc> - * <ja>@RestResource</ja>( - * properties={ - * <ja>@Property</ja>( - * name=<jsf>BEAN_examples</jsf>, - * value=<js>"{'org.apache.juneau.examples.rest.petstore.PetCreate': {name:'Doggie',price:9.99,species:'Dog',tags:['friendly','cute']}}"</js> - * ) - * } - * ) - * </p> - * - * <h5 class='section'>Used for:</h5> - * <ul class='spaced-list'> - * <li> - * Server-side generated Swagger documentation. - * </ul> - * - * <h5 class='section'>See also:</h5> - * <ul> - * <li class='ja'>{@link Example} - * <li class='jc'>{@link BeanContext} - * <ul> - * <li class='jf'>{@link BeanContext#BEAN_examples BEAN_examples} - * </ul> - * <li class='jc'>{@link JsonSchemaSerializer} - * <ul> - * <li class='jf'>{@link JsonSchemaSerializer#JSONSCHEMA_addExamplesTo JSONSCHEMA_addExamplesTo} - * <li class='jf'>{@link JsonSchemaSerializer#JSONSCHEMA_allowNestedExamples JSONSCHEMA_allowNestedExamples} - * </ul> - * </ul> - * - * <h5 class='section'>Notes:</h5> - * <ul class='spaced-list'> - * <li> - * The format is any <a class='doclink' href='../../../../../overview-summary.html#juneau-marshall.JsonDetails.SimplifiedJson'>Simplified JSON</a> if the object can be converted to a POJO using {@link JsonParser#DEFAULT} or a simple String if the object - * has a schema associated with it can be converted from a String. - * <br>Multiple lines are concatenated with newlines. - * <li> - * The format of this object can also be a simple String if the body has a schema associated with it, meaning it's meant to be treated as an HTTP part. - * <li> - * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a> - * (e.g. <js>"$L{my.localized.variable}"</js>). - * </ul> - */ - String[] example() default {}; - - /** - * Serialized examples of the body of a response. - * - * <p> - * This is a <a class='doclink' href='../../../../../overview-summary.html#juneau-marshall.JsonDetails.SimplifiedJson'>Simplified JSON</a> object whose keys are media types and values are string representations of that value. - * - * <p> - * In general you won't need to populate this value directly since it will automatically be calculated based on the value provided in the {@link #example()} field. - * <br>However, this field allows you to override the behavior and show examples for only specified media types or different examples for different media types. - * - * <p class='bcode w800'> - * <jc>// A JSON representation of a PetCreate object.</jc> - * <ja>@ResponseBody</ja>( - * examples={ - * <js>"'application/json':'{name:\\'Doggie\\',species:\\'Dog\\'}',"</js>, - * <js>"'text/uon':'(name:Doggie,species=Dog)'"</js> - * } - * ) - * </p> - * - * <h5 class='section'>Used for:</h5> - * <ul class='spaced-list'> - * <li> - * Server-side generated Swagger documentation. - * </ul> - * - * <h5 class='section'>Notes:</h5> - * <ul class='spaced-list'> - * <li> - * The format is a <a class='doclink' href='../../../../../overview-summary.html#juneau-marshall.JsonDetails.SimplifiedJson'>Simplified JSON</a> object with string keys (media type) and string values (example for that media type) . - * <li> - * The leading/trailing <code>{ }</code> characters are optional. - * <li> - * Multiple lines are concatenated with newlines so that you can format the value to be readable: - * <li> - * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a> - * (e.g. <js>"$L{my.localized.variable}"</js>). - * <li> - * Resolution of variables is delayed until request time and occurs before parsing. - * <br>This allows you to, for example, pull in a JSON construct from a properties file based on the locale of the HTTP request. - * </ul> - */ - String[] examples() default {}; - - /** - * Free-form value for the Swagger <a class="doclink" href="https://swagger.io/specification/v2/#responseObject">Response</a> object. - * - * <p> - * This is a <a class='doclink' href='../../../../../overview-summary.html#juneau-marshall.JsonDetails.SimplifiedJson'>Simplified JSON</a> object that makes up the swagger information for this field. - * - * <h5 class='section'>Notes:</h5> - * <ul class='spaced-list'> - * <li> - * Note that the only swagger field you can't specify using this value is <js>"code"</js> whose value needs to be known during servlet initialization. - * <li> - * The format is a <a class='doclink' href='../../../../../overview-summary.html#juneau-marshall.JsonDetails.SimplifiedJson'>Simplified JSON</a> object. - * <li> - * The leading/trailing <code>{ }</code> characters are optional. - * <br>The following two example are considered equivalent: - * <p class='bcode w800'> - * <ja>@ResponseBody</ja>(api=<js>"{schema:{type:'string'}}}"</js>) - * </p> - * <p class='bcode w800'> - * <ja>@ResponseBody</ja>(api=<js>"schema: {type:'string'}"</js>) - * </p> - * <li> - * Multiple lines are concatenated with newlines so that you can format the value to be readable. - * <li> - * Supports <a class="doclink" href="../../../../../overview-summary.html#DefaultRestSvlVariables">initialization-time and request-time variables</a> - * (e.g. <js>"$L{my.localized.variable}"</js>). - * <li> - * Values defined in this field supersede values pulled from the Swagger JSON file and are superseded by individual values defined on this annotation. - * </ul> - */ - String[] api() default {}; -} +public @interface ResponseBody {} diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java index 08e9500..87cdd19 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java @@ -140,7 +140,6 @@ public class HttpPartSchema { * <li>{@link Path} * <li>{@link Response} * <li>{@link ResponseHeader} - * <li>{@link ResponseBody} * <li>{@link HasQuery} * <li>{@link HasFormData} * </ul> @@ -169,7 +168,6 @@ public class HttpPartSchema { * <li>{@link Path} * <li>{@link Response} * <li>{@link ResponseHeader} - * <li>{@link ResponseBody} * <li>{@link HasQuery} * <li>{@link HasFormData} * </ul> diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java index 3ecef7e..1ad58f9 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchemaBuilder.java @@ -108,8 +108,6 @@ public class HttpPartSchemaBuilder { apply((Path)a); else if (a instanceof Response) apply((Response)a); - else if (a instanceof ResponseBody) - apply((ResponseBody)a); else if (a instanceof ResponseHeader) apply((ResponseHeader)a); else if (a instanceof HasQuery) @@ -134,13 +132,6 @@ public class HttpPartSchemaBuilder { return this; } - HttpPartSchemaBuilder apply(ResponseBody a) { - serializer(a.partSerializer()); - usePartSerializer(AnnotationUtils.usePartSerializer(a)); - apply(a.schema()); - return this; - } - HttpPartSchemaBuilder apply(Header a) { name(a.value()); name(a.name()); diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java index 17ccdea..0554fd8 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java @@ -37,16 +37,70 @@ public class ResponseBeanMeta { /** * Create metadata from specified class. * - * @param c The class annotated with {@link Response}. + * @param t The class annotated with {@link Response}. * @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 Response}. */ - public static ResponseBeanMeta create(Class<?> c, PropertyStore ps) { - if (! hasAnnotation(Response.class, c)) + public static ResponseBeanMeta create(Type t, PropertyStore ps) { + if (! hasAnnotation(Response.class, t)) return null; - return new ResponseBeanMeta.Builder(ps).apply(c).build(); + Builder b = new Builder(ps); + if (Value.isType(t)) + b.apply(Value.getParameterType(t)); + else + b.apply(t); + for (Response r : getAnnotationsParentFirst(Response.class, t)) + b.apply(r); + return b.build(); + } + + /** + * Create metadata from specified method return. + * + * @param m The method annotated with {@link Response}. + * @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 Response}. + */ + public static ResponseBeanMeta create(Method m, PropertyStore ps) { + if (! hasAnnotation(Response.class, m)) + return null; + Builder b = new Builder(ps); + Type t = m.getGenericReturnType(); + if (Value.isType(t)) + b.apply(Value.getParameterType(t)); + else + b.apply(t); + for (Response r : getAnnotationsParentFirst(Response.class, m)) + b.apply(r); + return b.build(); + } + + /** + * Create metadata from specified method parameter. + * + * @param m The method containing the parameter annotated with {@link Response}. + * @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 class, or <jk>null</jk> if class not annotated with {@link Response}. + */ + public static ResponseBeanMeta create(Method m, int i, PropertyStore ps) { + if (! hasAnnotation(Response.class, m, i)) + return null; + Builder b = new Builder(ps); + Type t = m.getGenericParameterTypes()[i]; + if (Value.isType(t)) + b.apply(Value.getParameterType(t)); + else + b.apply(t); + for (Response r : getAnnotationsParentFirst(Response.class, m, i)) + b.apply(r); + return b.build(); } //----------------------------------------------------------------------------------------------------------------- @@ -56,8 +110,7 @@ public class ResponseBeanMeta { private final ClassMeta<?> cm; private final int code; private final Map<String,ResponseBeanPropertyMeta> headerMethods; - private final Method statusMethod; - private final ResponseBeanPropertyMeta bodyMethod; + private final Method statusMethod, bodyMethod; private final HttpPartSerializer partSerializer; private final HttpPartSchema schema; private final boolean usePartSerializer; @@ -76,8 +129,7 @@ public class ResponseBeanMeta { } this.headerMethods = Collections.unmodifiableMap(hm); - - this.bodyMethod = b.bodyMethod == null ? null : b.bodyMethod.build(partSerializer); + this.bodyMethod = b.bodyMethod; this.statusMethod = b.statusMethod; } @@ -90,15 +142,15 @@ public class ResponseBeanMeta { HttpPartSchemaBuilder schema = HttpPartSchema.create(); Map<String,ResponseBeanPropertyMeta.Builder> headerMethods = new LinkedHashMap<>(); - ResponseBeanPropertyMeta.Builder bodyMethod; + Method bodyMethod; Method statusMethod; Builder(PropertyStore ps) { this.ps = ps; } - Builder apply(Class<?> c) { - apply(getAnnotation(Response.class, c)); + Builder apply(Type t) { + Class<?> c = ClassUtils.toClass(t); this.cm = BeanContext.DEFAULT.getClassMeta(c); for (Method m : ClassUtils.getAllMethods(c, false)) { if (isAll(m, PUBLIC, HAS_NO_ARGS)) { @@ -129,9 +181,7 @@ public class ResponseBeanMeta { Class<?> rt = m.getReturnType(); if (rt == void.class) throw new InvalidAnnotationException("Invalid return type for @ResponseBody annotation on method. Method=''{0}''", m); - ResponseBody a = getAnnotation(ResponseBody.class, m); - HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a); - bodyMethod = ResponseBeanPropertyMeta.create().partType(HttpPartType.BODY).apply(s).getter(m); + bodyMethod = m; } if (hasAnnotation(Body.class, m)) throw new InvalidAnnotationException("@Body annotation cannot be used in a @Response bean. Use @ResponseBody instead. Method=''{0}''", m); @@ -142,17 +192,16 @@ public class ResponseBeanMeta { return this; } - Builder apply(Response rb) { - if (rb != null) { - if (rb.partSerializer() != HttpPartSerializer.Null.class) - partSerializer = rb.partSerializer(); - if (rb.usePartSerializer()) - usePartSerializer = true; - if (rb.value().length > 0) - code = rb.value()[0]; - if (rb.code().length > 0) - code = rb.code()[0]; - schema.apply(rb.schema()); + Builder apply(Response a) { + if (a != null) { + if (a.partSerializer() != HttpPartSerializer.Null.class) + partSerializer = a.partSerializer(); + if (a.value().length > 0) + code = a.value()[0]; + if (a.code().length > 0) + code = a.code()[0]; + usePartSerializer = AnnotationUtils.usePartSerializer(a); + schema.apply(a.schema()); } return this; } @@ -190,18 +239,18 @@ public class ResponseBeanMeta { } /** - * Returns metadata about the <ja>@ResponseBody</ja>-annotated method. + * Returns the <ja>@ResponseBody</ja>-annotated method. * - * @return Metadata about the <ja>@ResponseBody</ja>-annotated method, or <jk>null</jk> if it doesn't exist. + * @return The <ja>@ResponseBody</ja>-annotated method, or <jk>null</jk> if it doesn't exist. */ - public ResponseBeanPropertyMeta getBodyMethod() { + public Method getBodyMethod() { return bodyMethod; } /** - * Returns metadata about the <ja>@ResponseBody</ja>-annotated method. + * Returns the <ja>@ResponseStatus</ja>-annotated method. * - * @return Metadata about the <ja>@ResponseBody</ja>-annotated method, or <jk>null</jk> if it doesn't exist. + * @return The <ja>@ResponseStatus</ja>-annotated method, or <jk>null</jk> if it doesn't exist. */ public Method getStatusMethod() { return statusMethod; diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java index c61c5ab..4ee46ca 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java @@ -2485,7 +2485,17 @@ public final class ClassUtils { } } - private static Class<?> toClass(Type t) { + /** + * Returns the specified type as a <code>Class</code>. + * + * <p> + * If it's already a <code>Class</code>, it just does a cast. + * <br>If it's a <code>ParameterizedType</code>, it returns the raw type. + * + * @param t The type to convert. + * @return The type converted to a <code>Class</code>, or <jk>null</jk> if it could not be converted. + */ + public static Class<?> toClass(Type t) { if (t instanceof Class) return (Class<?>)t; if (t instanceof ParameterizedType) { 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 4771c2e..aba79f1 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 @@ -4326,8 +4326,6 @@ public final class RestContext extends BeanContext { rp[i] = new RestParamDefaults.ResponseBeanObject(method, i, ps); } else if (hasAnnotation(ResponseHeader.class, method, i)) { rp[i] = new RestParamDefaults.ResponseHeaderObject(method, i, ps); - } else if (hasAnnotation(ResponseBody.class, method, i)) { - rp[i] = new RestParamDefaults.ResponseBodyObject(method, i, ps); } else if (hasAnnotation(ResponseStatus.class, method, i)) { rp[i] = new RestParamDefaults.ResponseStatusObject(method, t); } else if (hasAnnotation(HasFormData.class, method, i)) { diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java index fc968ec..d588cd5 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java @@ -79,7 +79,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod> { final Map<Class<?>,ResponseBeanMeta> responseBeanMetas = new ConcurrentHashMap<>(); final Map<Class<?>,ResponsePartMeta> headerPartMetas = new ConcurrentHashMap<>(); final Map<Class<?>,ResponsePartMeta> bodyPartMetas = new ConcurrentHashMap<>(); - final ResponsePartMeta returnBodyMeta; + final ResponseBeanMeta responseMeta; RestJavaMethod(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException { Builder b = new Builder(servlet, method, context); @@ -107,7 +107,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod> { this.priority = b.priority; this.supportedAcceptTypes = b.supportedAcceptTypes; this.supportedContentTypes = b.supportedContentTypes; - this.returnBodyMeta = b.returnBodyMeta; + this.responseMeta = b.responseMeta; this.widgets = unmodifiableMap(b.widgets); } @@ -130,7 +130,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod> { Integer priority; Map<String,Widget> widgets; List<MediaType> supportedAcceptTypes, supportedContentTypes; - ResponsePartMeta returnBodyMeta; + ResponseBeanMeta responseMeta; Builder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException { String sig = method.getDeclaringClass().getName() + '.' + method.getName(); @@ -410,10 +410,8 @@ public class RestJavaMethod implements Comparable<RestJavaMethod> { methodParams = context.findParams(method, pathPattern, false); - if (hasAnnotation(ResponseBody.class, method)) { - HttpPartSchema s = HttpPartSchema.create(ResponseBody.class, method); - returnBodyMeta = new ResponsePartMeta(HttpPartType.BODY, s, createPartSerializer(s.getSerializer(), serializers.getPropertyStore(), partSerializer)); - } + if (hasAnnotation(Response.class, method)) + responseMeta = ResponseBeanMeta.create(method, serializers.getPropertyStore()); // Need this to access methods in anonymous inner classes. setAccessible(method, true); 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 0cb7a14..355faea 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 @@ -673,42 +673,12 @@ class RestParamDefaults { } } - static final class ResponseBodyObject extends RestMethodParam { - final ResponsePartMeta rpm; - - protected ResponseBodyObject(Method m, int i, PropertyStore ps) { - super(RESPONSE_BODY, m, i); - HttpPartSchema schema = HttpPartSchema.create(ResponseBody.class, method, i); - this.rpm = new ResponsePartMeta(HttpPartType.BODY, schema, createPartSerializer(schema.getSerializer(), ps)); - - if (getTypeClass() != Value.class) - throw new InternalServerError("Invalid type {0} specified with @ResponseHeader annotation. It must be Value.", type); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override /* RestMethodParam */ - public Object resolve(final RestRequest req, final RestResponse res) throws Exception { - Value<Object> v = (Value<Object>)getTypeClass().newInstance(); - v.listener(new ValueListener() { - @Override - public void onSet(Object o) { - ResponsePartMeta rpm = req.getResponseBodyMeta(o); - if (rpm == null) - rpm = ResponseBodyObject.this.rpm; - res.setResponseBodyMeta(rpm); - res.setOutput(o); - } - }); - return v; - } - } - static final class ResponseBeanObject extends RestMethodParam { final ResponseBeanMeta rbm; protected ResponseBeanObject(Method m, int i, PropertyStore ps) { super(RESPONSE, m, i); - this.rbm = ResponseBeanMeta.create(m.getParameterTypes()[i], ps); + this.rbm = ResponseBeanMeta.create(m, i, ps); if (getTypeClass() != Value.class) throw new InternalServerError("Invalid type {0} specified with @Response annotation. It must be Value.", type); } diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java index b38221d..70947b4 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java @@ -68,7 +68,6 @@ public final class RestResponse extends HttpServletResponseWrapper { private HtmlDocBuilder htmlDocBuilder; private ResponseBeanMeta resBeanMeta; - private ResponsePartMeta resBodyMeta; /** * Constructor. @@ -121,7 +120,7 @@ public final class RestResponse extends HttpServletResponseWrapper { throw new NotAcceptable("No supported charsets in header ''Accept-Charset'': ''{0}''", request.getHeader("Accept-Charset")); super.setCharacterEncoding(charset); - this.resBodyMeta = rjm.returnBodyMeta; + this.resBeanMeta = rjm.responseMeta; } /** @@ -539,28 +538,6 @@ public final class RestResponse extends HttpServletResponseWrapper { } /** - * Returns the metadata about this response body. - * - * @return - * The metadata about this response. - * <jk>Never <jk>null</jk>. - */ - public ResponsePartMeta getResponseBodyMeta() { - return resBodyMeta; - } - - /** - * Sets metadata about this response body. - * - * @param rpm The metadata about this response body. - * @return This object (for method chaining). - */ - public RestResponse setResponseBodyMeta(ResponsePartMeta rpm) { - this.resBodyMeta = rpm; - return this; - } - - /** * Returns <jk>true</jk> if this response object is of the specified type. * * @param c The type to check against. diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java index 1bed25a..2cb0d7a 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerGenerator.java @@ -379,18 +379,6 @@ final class SwaggerGenerator { } } - if (hasAnnotation(ResponseBody.class, m)) { - ObjectMap om = responses.getObjectMap("200", true); - boolean usePS = false; - for (ResponseBody a : getAnnotationsParentFirst(ResponseBody.class, m)) { - merge(om, a); - usePS |= usePartSerializer(a); - } - if (usePS && ! om.containsKey("schema")) - om.appendSkipEmpty("schema", getSchema(om.getObjectMap("schema", true), m.getGenericReturnType())); - addXExamples(sm, om, "ok", m.getGenericReturnType()); - } - if (hasAnnotation(Response.class, m)) { boolean usePS = false; for (Response a : getAnnotationsParentFirst(Response.class, m)) { @@ -441,19 +429,6 @@ final class SwaggerGenerator { } } } - - } else if (in == RESPONSE_BODY) { - ObjectMap response = responses.getObjectMap("200", true); - boolean usePS = false; - for (ResponseBody a : getAnnotationsParentFirst(ResponseBody.class, mp.method, mp.index)) { - merge(response, a); - usePS |= usePartSerializer(a); - } - if (usePS && ! response.containsKey("schema")) { - Type type = Value.getParameterType(mp.type); - if (type != null) - response.appendSkipEmpty("schema", getSchema(response.getObjectMap("schema", true), type)); - } } } @@ -1088,19 +1063,6 @@ final class SwaggerGenerator { ; } - private ObjectMap merge(ObjectMap om, ResponseBody a) throws ParseException { - if (empty(a)) - return om; - om = newMap(om); - if (a.api().length > 0) - om.putAll(parseMap(a.api())); - return om - .appendSkipEmpty("x-example", resolve(a.example())) - .appendSkipEmpty("examples", joinnl(a.examples())) - .appendSkipEmpty("schema", merge(om.getObjectMap("schema"), a.schema())) - ; - } - private ObjectMap merge(ObjectMap om, Response a) throws ParseException { if (empty(a)) return om; diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java index c9004a6..8b9cf43 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java @@ -86,14 +86,14 @@ public class DefaultHandler implements ResponseHandler { } } - ResponseBeanPropertyMeta m = rbm.getBodyMethod(); + Method m = rbm.getBodyMethod(); boolean usePartSerializer = rbm.isUsePartSerializer(); HttpPartSchema schema = rbm.getSchema(); if (m != null) { try { - o = m.getGetter().invoke(o); - schema = m.getSchema(); + o = m.invoke(o); + schema = rbm.getSchema(); usePartSerializer |= schema.isUsePartSerializer(); } catch (Exception e) { throw new InternalServerError(e, "Could not get body."); @@ -117,29 +117,6 @@ public class DefaultHandler implements ResponseHandler { } } - ResponsePartMeta rpm = res.getResponseBodyMeta(); - if (rpm == null) - rpm = req.getResponseBodyMeta(o); - - if (rpm != null && rpm.getSchema().isUsePartSerializer()) { - if (res.getContentType() == null) - res.setContentType("text/plain"); - HttpPartSerializer ps = rpm.getSerializer(); - if (ps == null) - ps = req.getPartSerializer(); - if (ps != null) { - try (FinishablePrintWriter w = res.getNegotiatedWriter()) { - w.append(ps.serialize(HttpPartType.BODY, rpm.getSchema(), o)); - w.flush(); - w.finish(); - } catch (SchemaValidationException | SerializeException e) { - throw new InternalServerError(e); - } - return true; - } - } - - if (sm != null) { Serializer s = sm.getSerializer(); MediaType mediaType = res.getMediaType(); diff --git a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseAnnotationTest.java b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseAnnotationTest.java index c2e6a59..cd03b9c 100644 --- a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseAnnotationTest.java +++ b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseAnnotationTest.java @@ -22,9 +22,11 @@ import org.apache.juneau.*; import org.apache.juneau.dto.swagger.*; import org.apache.juneau.http.annotation.*; import org.apache.juneau.httppart.*; +import org.apache.juneau.json.*; import org.apache.juneau.rest.*; import org.apache.juneau.rest.mock.*; import org.apache.juneau.serializer.*; +import org.apache.juneau.utils.*; import org.junit.*; import org.junit.runners.*; @@ -39,11 +41,15 @@ public class ResponseAnnotationTest { // Setup //================================================================================================================= - private static Swagger getSwagger(Object resource) throws Exception { - RestContext rc = RestContext.create(resource).build(); - RestRequest req = rc.getCallHandler().createRequest(new MockServletRequest()); - RestInfoProvider ip = rc.getInfoProvider(); - return ip.getSwagger(req); + private static Swagger getSwagger(Object resource) { + try { + RestContext rc = RestContext.create(resource).build(); + RestRequest req = rc.getCallHandler().createRequest(new MockServletRequest()); + RestInfoProvider ip = rc.getInfoProvider(); + return ip.getSwagger(req); + } catch (Exception e) { + throw new RuntimeException(e); + } } @@ -126,13 +132,13 @@ public class ResponseAnnotationTest { @RestResource(partSerializer=XPartSerializer.class) public static class B { - @ResponseBody(usePartSerializer=true) + @Response(usePartSerializer=true) @RestMethod(name=GET,path="/useOnMethod") public String b01() { return "foo"; } - @ResponseBody(usePartSerializer=false) + @Response(usePartSerializer=false) @RestMethod(name=GET,path="/dontUseOnMethod") public String b02() { return "foo"; @@ -159,12 +165,12 @@ public class ResponseAnnotationTest { } @RestMethod(name=GET,path="/useOnParameter") - public void b07(@ResponseBody(usePartSerializer=true) Value<String> value) { + public void b07(@Response(usePartSerializer=true) Value<String> value) { value.set("foo"); } @RestMethod(name=GET,path="/dontUseOnParameter") - public void b08(@ResponseBody(usePartSerializer=false) Value<String> value) { + public void b08(@Response(usePartSerializer=false) Value<String> value) { value.set("foo"); } @@ -244,7 +250,7 @@ public class ResponseAnnotationTest { @RestResource public static class C { - @ResponseBody(partSerializer=XPartSerializer.class) + @Response(partSerializer=XPartSerializer.class) @RestMethod(name=GET,path="/useOnMethod") public String c01() { return "foo"; @@ -277,12 +283,12 @@ public class ResponseAnnotationTest { } @RestMethod(name=GET,path="/useOnParameter") - public void c07(@ResponseBody(partSerializer=XPartSerializer.class) Value<String> value) { + public void c07(@Response(partSerializer=XPartSerializer.class) Value<String> value) { value.set("foo"); } @RestMethod(name=GET,path="/dontUseOnParameter") - public void c08(@ResponseBody Value<String> value) { + public void c08(@Response Value<String> value) { value.set("foo"); } @@ -362,7 +368,7 @@ public class ResponseAnnotationTest { @RestResource public static class D { - @ResponseBody(schema=@Schema(collectionFormat="pipes"),usePartSerializer=true) + @Response(schema=@Schema(collectionFormat="pipes"),usePartSerializer=true) @RestMethod(name=GET,path="/useOnMethod") public String[] d01() { return new String[]{"foo","bar"}; @@ -379,11 +385,11 @@ public class ResponseAnnotationTest { } @RestMethod(name=GET,path="/useOnParameter") - public void d04(@ResponseBody(schema=@Schema(collectionFormat="pipes"),usePartSerializer=true) Value<String[]> value) { + public void d04(@Response(schema=@Schema(collectionFormat="pipes"),usePartSerializer=true) Value<String[]> value) { value.set(new String[]{"foo","bar"}); } - @ResponseBody(schema=@Schema(type="string",format="byte"),usePartSerializer=true) + @Response(schema=@Schema(type="string",format="byte"),usePartSerializer=true) @RestMethod(name=GET,path="/useOnMethodBytes") public byte[] d05() { return "foo".getBytes(); @@ -401,7 +407,7 @@ public class ResponseAnnotationTest { @RestMethod(name=GET,path="/useOnParameterBytes") - public void d08(@ResponseBody(schema=@Schema(type="string",format="byte"),usePartSerializer=true) Value<byte[]> value) { + public void d08(@Response(schema=@Schema(type="string",format="byte"),usePartSerializer=true) Value<byte[]> value) { value.set("foo".getBytes()); } @@ -479,6 +485,377 @@ public class ResponseAnnotationTest { } + //----------------------------------------------------------------------------------------------------------------- + // Basic + //----------------------------------------------------------------------------------------------------------------- + + @RestResource + public static class E { + + @RestMethod + public void m01(@Response Value<E01> body) { + body.set(new E01()); + } + + @RestMethod + public void m02(Value<E02> body) { + body.set(new E02()); + } + + @RestMethod + @Response + public E01 m03() { + return new E01(); + } + + @RestMethod + public E02 m04() { + return new E02(); + } + } + + public static class E01 { + @Override + public String toString() {return "foo";} + } + + @Response + public static class E02 { + @Override + public String toString() {return "foo";} + } + + static MockRest e = MockRest.create(E.class); + + @Test + public void e01_basic_onParameter() throws Exception { + e.get("/m01").execute().assertStatus(200).assertBody("foo"); + } + @Test + public void e02_basic_onType() throws Exception { + e.get("/m02").execute().assertStatus(200).assertBody("foo"); + } + @Test + public void e03_basic_onMethod() throws Exception { + e.get("/m03").execute().assertStatus(200).assertBody("foo"); + } + @Test + public void e04_basic_onReturnedType() throws Exception { + e.get("/m04").execute().assertStatus(200).assertBody("foo"); + } + + //----------------------------------------------------------------------------------------------------------------- + // Basic swagger + //----------------------------------------------------------------------------------------------------------------- + + @RestResource + public static class F { + + @RestMethod + public void m01(@Response(schema=@Schema(description="f01", collectionFormat="pipes")) Value<List<Integer>> body) { + body.set(AList.create(1,2)); + } + + @RestMethod + public void m02(Value<F01> body) { + body.set(new F01()); + } + + @RestMethod + @Response(schema=@Schema(description="f03", collectionFormat="pipes")) + public List<Integer> m03() { + return AList.create(1,2); + } + + @RestMethod + public F01 m04() { + return new F01(); + } + } + + @Response(schema=@Schema(description="f01", collectionFormat="pipes")) + public static class F01 extends ArrayList<Integer> { + public F01() { + add(1); + add(2); + } + } + + static MockRest f = MockRest.create(F.class); + static Swagger sf = getSwagger(new F()); + + @Test + public void f01a_basic_onParameter() throws Exception { + f.get("/m01").execute().assertStatus(200).assertBody("1|2"); + } + @Test + public void f01b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sf.getResponseInfo("/m01", "get", 200); + assertObjectEquals("{description:'OK',schema:{description:'f01',collectionFormat:'pipes'}}", ri); + } + @Test + public void f02a_basic_onType() throws Exception { + f.get("/m02").execute().assertStatus(200).assertBody("1|2"); + } + @Test + public void f02b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sf.getResponseInfo("/m02", "get", 200); + assertObjectEquals("{description:'OK',schema:{description:'f01',collectionFormat:'pipes'}}", ri); + } + @Test + public void f03a_basic_onMethod() throws Exception { + f.get("/m03").execute().assertStatus(200).assertBody("1|2"); + } + @Test + public void f03b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sf.getResponseInfo("/m03", "get", 200); + assertObjectEquals("{description:'OK',schema:{description:'f03',collectionFormat:'pipes'}}", ri); + } + @Test + public void f04a_basic_onReturnedType() throws Exception { + f.get("/m04").execute().assertStatus(200).assertBody("1|2"); + } + @Test + public void f04b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sf.getResponseInfo("/m04", "get", 200); + assertObjectEquals("{description:'OK',schema:{description:'f01',collectionFormat:'pipes'}}", ri); + } + + //----------------------------------------------------------------------------------------------------------------- + // Test JSON Accept + //----------------------------------------------------------------------------------------------------------------- + + @RestResource(serializers=SimpleJsonSerializer.class) + public static class G { + + @RestMethod + public void m01(@Response Value<List<Integer>> body) { + body.set(AList.create(1,2)); + } + + @RestMethod + public void m02(Value<G01> body) { + body.set(new G01()); + } + + @RestMethod + @Response + public List<Integer> m03() { + return AList.create(1,2); + } + + @RestMethod + public G01 m04() { + return new G01(); + } + } + + @Response + public static class G01 extends ArrayList<Integer> { + public G01() { + add(1); + add(2); + } + } + + static MockRest g = MockRest.create(G.class); + static Swagger sg = getSwagger(new G()); + + @Test + public void g01a_basic_onParameter() throws Exception { + g.get("/m01").json().execute().assertStatus(200).assertBody("[1,2]"); + } + @Test + public void g01b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sg.getResponseInfo("/m01", "get", 200); + assertObjectEquals("{description:'OK'}", ri); + } + @Test + public void g02a_basic_onType() throws Exception { + g.get("/m02").json().execute().assertStatus(200).assertBody("[1,2]"); + } + @Test + public void g02b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sg.getResponseInfo("/m02", "get", 200); + assertObjectEquals("{description:'OK'}", ri); + } + @Test + public void g03a_basic_onMethod() throws Exception { + g.get("/m03").json().execute().assertStatus(200).assertBody("[1,2]"); + } + @Test + public void g03b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sg.getResponseInfo("/m03", "get", 200); + assertObjectEquals("{description:'OK'}", ri); + } + @Test + public void g04a_basic_onReturnedType() throws Exception { + g.get("/m04").json().execute().assertStatus(200).assertBody("[1,2]"); + } + @Test + public void g04b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sg.getResponseInfo("/m04", "get", 200); + assertObjectEquals("{description:'OK'}", ri); + } + + //================================================================================================================= + // PartSerializers + //================================================================================================================= + + //----------------------------------------------------------------------------------------------------------------- + // @ResponseBody(usePartSerializer), partSerializer on class + //----------------------------------------------------------------------------------------------------------------- + + @RestResource(partSerializer=XPartSerializer.class) + public static class H { + + @RestMethod + public void m01(@Response(usePartSerializer=true) Value<List<Integer>> body) { + body.set(AList.create(1,2)); + } + + @RestMethod + public void m02(Value<H01> body) { + body.set(new H01()); + } + + @RestMethod + @Response(usePartSerializer=true) + public List<Integer> m03() { + return AList.create(1,2); + } + + @RestMethod + public H01 m04() { + return new H01(); + } + } + + @Response(usePartSerializer=true) + public static class H01 extends ArrayList<Integer> { + public H01() { + add(1); + add(2); + } + } + + static MockRest h = MockRest.create(H.class); + static Swagger sh = getSwagger(new H()); + + @Test + public void h01a_basic_onParameter() throws Exception { + h.get("/m01").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void h01b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sh.getResponseInfo("/m01", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + @Test + public void h02a_basic_onType() throws Exception { + h.get("/m02").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void h02b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sh.getResponseInfo("/m02", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + @Test + public void h03a_basic_onMethod() throws Exception { + h.get("/m03").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void h03b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sh.getResponseInfo("/m03", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + @Test + public void h04a_basic_onReturnedType() throws Exception { + h.get("/m04").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void h04b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = sh.getResponseInfo("/m04", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + + //----------------------------------------------------------------------------------------------------------------- + // @ResponseBody(usePartSerializer), partSerializer on part. + //----------------------------------------------------------------------------------------------------------------- + + @RestResource + public static class I { + + @RestMethod + public void m01(@Response(partSerializer=XPartSerializer.class) Value<List<Integer>> body) { + body.set(AList.create(1,2)); + } + + @RestMethod + public void m02(Value<I01> body) { + body.set(new I01()); + } + + @RestMethod + @Response(partSerializer=XPartSerializer.class) + public List<Integer> m03() { + return AList.create(1,2); + } + + @RestMethod + public I01 m04() { + return new I01(); + } + } + + @Response(partSerializer=XPartSerializer.class) + public static class I01 extends ArrayList<Integer> { + public I01() { + add(1); + add(2); + } + } + + static MockRest i = MockRest.create(I.class); + static Swagger si = getSwagger(new I()); + + @Test + public void i01a_basic_onParameter() throws Exception { + i.get("/m01").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void i01b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = si.getResponseInfo("/m01", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + @Test + public void i02a_basic_onType() throws Exception { + i.get("/m02").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void i02b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = si.getResponseInfo("/m02", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + @Test + public void i03a_basic_onMethod() throws Exception { + i.get("/m03").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void i03b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = si.getResponseInfo("/m03", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + @Test + public void i04a_basic_onReturnedType() throws Exception { + i.get("/m04").execute().assertStatus(200).assertBody("x[1, 2]x"); + } + @Test + public void i04b_basic_onParameter_swagger() throws Exception { + ResponseInfo ri = si.getResponseInfo("/m04", "get", 200); + assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); + } + //================================================================================================================= // @Response on POJO //================================================================================================================= diff --git a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseBodyAnnotationTest.java b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseBodyAnnotationTest.java index 0324663..6cbbdd4 100644 --- a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseBodyAnnotationTest.java +++ b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseBodyAnnotationTest.java @@ -12,441 +12,13 @@ // *************************************************************************************************************************** package org.apache.juneau.rest.annotation; -import static org.apache.juneau.testutils.TestUtils.*; - -import java.util.*; - -import org.apache.juneau.*; -import org.apache.juneau.dto.swagger.*; -import org.apache.juneau.http.annotation.*; -import org.apache.juneau.httppart.*; -import org.apache.juneau.json.*; -import org.apache.juneau.rest.*; -import org.apache.juneau.rest.mock.*; -import org.apache.juneau.serializer.*; -import org.apache.juneau.utils.*; import org.junit.*; import org.junit.runners.*; /** * Tests the @Response annotation. */ -@SuppressWarnings({"javadoc","serial"}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ResponseBodyAnnotationTest { - //================================================================================================================= - // Setup - //================================================================================================================= - - private static Swagger getSwagger(Object resource) { - try { - RestContext rc = RestContext.create(resource).build(); - RestRequest req = rc.getCallHandler().createRequest(new MockServletRequest()); - RestInfoProvider ip = rc.getInfoProvider(); - return ip.getSwagger(req); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - - //================================================================================================================= - // Basic tests - //================================================================================================================= - - //----------------------------------------------------------------------------------------------------------------- - // Basic - //----------------------------------------------------------------------------------------------------------------- - - @RestResource - public static class A { - - @RestMethod - public void m01(@ResponseBody Value<A01> body) { - body.set(new A01()); - } - - @RestMethod - public void m02(Value<A02> body) { - body.set(new A02()); - } - - @RestMethod - @ResponseBody - public A01 m03() { - return new A01(); - } - - @RestMethod - public A02 m04() { - return new A02(); - } - } - - public static class A01 { - @Override - public String toString() {return "foo";} - } - - @ResponseBody - public static class A02 { - @Override - public String toString() {return "foo";} - } - - static MockRest a = MockRest.create(A.class); - - @Test - public void a01_basic_onParameter() throws Exception { - a.get("/m01").execute().assertStatus(200).assertBody("foo"); - } - @Test - public void a02_basic_onType() throws Exception { - a.get("/m02").execute().assertStatus(200).assertBody("foo"); - } - @Test - public void a03_basic_onMethod() throws Exception { - a.get("/m03").execute().assertStatus(200).assertBody("foo"); - } - @Test - public void a04_basic_onReturnedType() throws Exception { - a.get("/m04").execute().assertStatus(200).assertBody("foo"); - } - - //----------------------------------------------------------------------------------------------------------------- - // Basic swagger - //----------------------------------------------------------------------------------------------------------------- - - @RestResource - public static class B { - - @RestMethod - public void m01(@ResponseBody(schema=@Schema(description="b01", collectionFormat="pipes")) Value<List<Integer>> body) { - body.set(AList.create(1,2)); - } - - @RestMethod - public void m02(Value<B01> body) { - body.set(new B01()); - } - - @RestMethod - @ResponseBody(schema=@Schema(description="b03", collectionFormat="pipes")) - public List<Integer> m03() { - return AList.create(1,2); - } - - @RestMethod - public B01 m04() { - return new B01(); - } - } - - @ResponseBody(schema=@Schema(description="b01", collectionFormat="pipes")) - public static class B01 extends ArrayList<Integer> { - public B01() { - add(1); - add(2); - } - } - - static MockRest b = MockRest.create(B.class); - static Swagger sb = getSwagger(new B()); - - @Test - public void b01a_basic_onParameter() throws Exception { - b.get("/m01").execute().assertStatus(200).assertBody("1|2"); - } - @Test - public void b01b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sb.getResponseInfo("/m01", "get", 200); - assertObjectEquals("{description:'OK',schema:{description:'b01',collectionFormat:'pipes'}}", ri); - } - @Test - public void b02a_basic_onType() throws Exception { - b.get("/m02").execute().assertStatus(200).assertBody("1|2"); - } - @Test - public void b02b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sb.getResponseInfo("/m02", "get", 200); - assertObjectEquals("{description:'OK',schema:{description:'b01',collectionFormat:'pipes'}}", ri); - } - @Test - public void b03a_basic_onMethod() throws Exception { - b.get("/m03").execute().assertStatus(200).assertBody("1|2"); - } - @Test - public void b03b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sb.getResponseInfo("/m03", "get", 200); - assertObjectEquals("{description:'OK',schema:{description:'b03',collectionFormat:'pipes'}}", ri); - } - @Test - public void b04a_basic_onReturnedType() throws Exception { - b.get("/m04").execute().assertStatus(200).assertBody("1|2"); - } - @Test - public void b04b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sb.getResponseInfo("/m04", "get", 200); - assertObjectEquals("{description:'OK',schema:{description:'b01',collectionFormat:'pipes'}}", ri); - } - - //----------------------------------------------------------------------------------------------------------------- - // Test JSON Accept - //----------------------------------------------------------------------------------------------------------------- - - @RestResource(serializers=SimpleJsonSerializer.class) - public static class C { - - @RestMethod - public void m01(@ResponseBody Value<List<Integer>> body) { - body.set(AList.create(1,2)); - } - - @RestMethod - public void m02(Value<C01> body) { - body.set(new C01()); - } - - @RestMethod - @ResponseBody - public List<Integer> m03() { - return AList.create(1,2); - } - - @RestMethod - public C01 m04() { - return new C01(); - } - } - - @ResponseBody - public static class C01 extends ArrayList<Integer> { - public C01() { - add(1); - add(2); - } - } - - static MockRest c = MockRest.create(C.class); - static Swagger sc = getSwagger(new C()); - - @Test - public void c01a_basic_onParameter() throws Exception { - c.get("/m01").json().execute().assertStatus(200).assertBody("[1,2]"); - } - @Test - public void c01b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sc.getResponseInfo("/m01", "get", 200); - assertObjectEquals("{description:'OK'}", ri); - } - @Test - public void c02a_basic_onType() throws Exception { - c.get("/m02").json().execute().assertStatus(200).assertBody("[1,2]"); - } - @Test - public void c02b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sc.getResponseInfo("/m02", "get", 200); - assertObjectEquals("{description:'OK'}", ri); - } - @Test - public void c03a_basic_onMethod() throws Exception { - c.get("/m03").json().execute().assertStatus(200).assertBody("[1,2]"); - } - @Test - public void c03b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sc.getResponseInfo("/m03", "get", 200); - assertObjectEquals("{description:'OK'}", ri); - } - @Test - public void c04a_basic_onReturnedType() throws Exception { - c.get("/m04").json().execute().assertStatus(200).assertBody("[1,2]"); - } - @Test - public void c04b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sc.getResponseInfo("/m04", "get", 200); - assertObjectEquals("{description:'OK'}", ri); - } - - - //================================================================================================================= - // PartSerializers - //================================================================================================================= - - public static class XPartSerializer implements HttpPartSerializer { - @Override - public HttpPartSerializerSession createSession(SerializerSessionArgs args) { - return new HttpPartSerializerSession() { - @Override - public String serialize(HttpPartType partType, HttpPartSchema schema, Object value) throws SerializeException, SchemaValidationException { - return "x" + value + "x"; - } - }; - } - - @Override - public String serialize(HttpPartType partType, HttpPartSchema schema, Object value) throws SchemaValidationException, SerializeException { - return createSession(null).serialize(partType, schema, value); - } - - @Override - public String serialize(HttpPartSchema schema, Object value) throws SchemaValidationException, SerializeException { - return createSession(null).serialize(null, schema, value); - } - } - - //----------------------------------------------------------------------------------------------------------------- - // @ResponseBody(usePartSerializer), partSerializer on class - //----------------------------------------------------------------------------------------------------------------- - - @RestResource(partSerializer=XPartSerializer.class) - public static class D { - - @RestMethod - public void m01(@ResponseBody(usePartSerializer=true) Value<List<Integer>> body) { - body.set(AList.create(1,2)); - } - - @RestMethod - public void m02(Value<D01> body) { - body.set(new D01()); - } - - @RestMethod - @ResponseBody(usePartSerializer=true) - public List<Integer> m03() { - return AList.create(1,2); - } - - @RestMethod - public D01 m04() { - return new D01(); - } - } - - @ResponseBody(usePartSerializer=true) - public static class D01 extends ArrayList<Integer> { - public D01() { - add(1); - add(2); - } - } - - static MockRest d = MockRest.create(D.class); - static Swagger sd = getSwagger(new D()); - - @Test - public void d01a_basic_onParameter() throws Exception { - d.get("/m01").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void d01b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sd.getResponseInfo("/m01", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - @Test - public void d02a_basic_onType() throws Exception { - d.get("/m02").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void d02b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sd.getResponseInfo("/m02", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - @Test - public void d03a_basic_onMethod() throws Exception { - d.get("/m03").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void d03b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sd.getResponseInfo("/m03", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - @Test - public void d04a_basic_onReturnedType() throws Exception { - d.get("/m04").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void d04b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = sd.getResponseInfo("/m04", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - - //----------------------------------------------------------------------------------------------------------------- - // @ResponseBody(usePartSerializer), partSerializer on part. - //----------------------------------------------------------------------------------------------------------------- - - @RestResource - public static class E { - - @RestMethod - public void m01(@ResponseBody(partSerializer=XPartSerializer.class) Value<List<Integer>> body) { - body.set(AList.create(1,2)); - } - - @RestMethod - public void m02(Value<E01> body) { - body.set(new E01()); - } - - @RestMethod - @ResponseBody(partSerializer=XPartSerializer.class) - public List<Integer> m03() { - return AList.create(1,2); - } - - @RestMethod - public E01 m04() { - return new E01(); - } - } - - @ResponseBody(partSerializer=XPartSerializer.class) - public static class E01 extends ArrayList<Integer> { - public E01() { - add(1); - add(2); - } - } - - static MockRest e = MockRest.create(E.class); - static Swagger se = getSwagger(new E()); - - @Test - public void e01a_basic_onParameter() throws Exception { - e.get("/m01").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void e01b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = se.getResponseInfo("/m01", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - @Test - public void e02a_basic_onType() throws Exception { - e.get("/m02").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void e02b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = se.getResponseInfo("/m02", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - @Test - public void e03a_basic_onMethod() throws Exception { - e.get("/m03").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void e03b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = se.getResponseInfo("/m03", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - @Test - public void e04a_basic_onReturnedType() throws Exception { - e.get("/m04").execute().assertStatus(200).assertBody("x[1, 2]x"); - } - @Test - public void e04b_basic_onParameter_swagger() throws Exception { - ResponseInfo ri = se.getResponseInfo("/m04", "get", 200); - assertObjectEquals("{description:'OK',schema:{type:'array',items:{type:'integer',format:'int32'}}}", ri); - } - }