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 32ab891  Tests.
32ab891 is described below

commit 32ab891932efcb17631915b8d53405af73e41d80
Author: JamesBognar <[email protected]>
AuthorDate: Sun Aug 5 13:08:25 2018 -0400

    Tests.
---
 .../src/test/java/org/apache/juneau/ValueTest.java |  27 +-
 .../http/annotation/AnnotationUtilsTest.java       | 203 +++++++++
 .../org/apache/juneau/dto/swagger/Swagger.java     |  42 ++
 .../main/java/org/apache/juneau/BeanSession.java   |   1 -
 .../src/main/java/org/apache/juneau/Value.java     |  16 +-
 .../juneau/http/annotation/AnnotationUtils.java    |  63 ++-
 .../org/apache/juneau/http/annotation/Contact.java |   3 -
 .../juneau/http/annotation/ExternalDocs.java       |   6 +
 .../org/apache/juneau/http/annotation/Items.java   |   3 -
 .../org/apache/juneau/http/annotation/License.java |   3 -
 .../org/apache/juneau/http/annotation/RBody.java   |  21 -
 .../org/apache/juneau/http/annotation/RStatus.java |  20 -
 .../apache/juneau/http/annotation/Response.java    |   2 +-
 .../juneau/http/annotation/ResponseBody.java       |   2 +-
 .../juneau/http/annotation/ResponseHeader.java     |   2 +-
 .../org/apache/juneau/http/annotation/Schema.java  |   3 -
 .../apache/juneau/http/annotation/SubItems.java    |   3 -
 .../org/apache/juneau/http/annotation/Tag.java     |   3 -
 .../juneau/httppart/HttpPartSchemaBuilder.java     |   8 +-
 .../org/apache/juneau/internal/ClassUtils.java     | 187 +++++----
 juneau-doc/src/main/javadoc/overview.html          | 120 ++++--
 .../10.Transforms/02.PerMediaTypePojoSwaps.html    |  22 +-
 .../09.HttpPartAnnotations/08.RequestBean.html     |  63 +--
 .../09.HttpPartAnnotations/09.ResponseBody.html    |  75 ++++
 .../org/apache/juneau/rest/SwaggerGenerator.java   |  48 ++-
 .../juneau/rest/BasicRestInfoProviderTest.java     |   2 +-
 .../rest/annotation/ResponseAnnotationTest.java    |   6 +-
 .../annotation/ResponseBodyAnnotationTest.java     | 452 +++++++++++++++++++++
 28 files changed, 1167 insertions(+), 239 deletions(-)

diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ValueTest.java 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ValueTest.java
index 3414351..0a1ef1b 100644
--- 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ValueTest.java
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/ValueTest.java
@@ -14,6 +14,10 @@ package org.apache.juneau;
 
 import static org.junit.Assert.*;
 
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.internal.*;
 import org.junit.*;
 
 /**
@@ -21,6 +25,10 @@ import org.junit.*;
  */
 public class ValueTest {
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Value defined on parent class.
+       
//-----------------------------------------------------------------------------------------------------------------
+
        public static class A extends Value<A1>{}
        public static class A1 {}
 
@@ -29,14 +37,29 @@ public class ValueTest {
                assertEquals(A1.class, Value.getParameterType(A.class));
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Value used in parameter.
+       
//-----------------------------------------------------------------------------------------------------------------
        public static class B {
                public void b(Value<B1> b1) {};
-
        }
        public static class B1 {}
 
        @Test
-       public void testParameter() throws Exception {
+       public void testOnParameter() throws Exception {
                assertEquals(B1.class, 
Value.getParameterType(B.class.getMethod("b", Value.class), 0));
        }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Value used on parameter of parameterized-type.
+       
//-----------------------------------------------------------------------------------------------------------------
+       public interface C {
+               void m1(Value<List<Integer>> v);
+       }
+
+       @Test
+       public void testOnParameterType() throws Exception {
+               Type t = Value.getParameterType(C.class.getMethod("m1", 
Value.class), 0);
+               assertEquals("List<Integer>", ClassUtils.getSimpleName(t));
+       }
 }
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
new file mode 100644
index 0000000..f9a9937
--- /dev/null
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/http/annotation/AnnotationUtilsTest.java
@@ -0,0 +1,203 @@
+// 
***************************************************************************************************************************
+// * 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.http.annotation;
+
+import static org.junit.Assert.*;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+import static org.apache.juneau.http.annotation.AnnotationUtils.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+
+import org.apache.juneau.httppart.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AnnotationUtilsTest {
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Test empty checks
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Target(TYPE)
+       @Retention(RUNTIME)
+       public @interface X {
+               Contact x1() default @Contact;
+               ExternalDocs x2() default @ExternalDocs;
+               License x3() default @License;
+               Schema x4() default @Schema;
+               SubItems x5() default @SubItems;
+               Items x6() default @Items;
+       }
+
+       @Body
+       @Response
+       @ResponseBody
+       @ResponseHeader
+       @X
+       public static class A01 {
+               @Query @Header @FormData @Path @Schema
+               public int f1;
+       }
+
+       @Test
+       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);
+               assertTrue(empty(x.x1()));
+               assertTrue(empty(x.x2()));
+               assertTrue(empty(x.x3()));
+               assertTrue(empty(x.x4()));
+               assertTrue(empty(x.x5()));
+               assertTrue(empty(x.x6()));
+
+               Field f = A01.class.getField("f1");
+               assertTrue(empty(f.getAnnotation(Query.class)));
+               assertTrue(empty(f.getAnnotation(Header.class)));
+               assertTrue(empty(f.getAnnotation(FormData.class)));
+               assertTrue(empty(f.getAnnotation(Path.class)));
+       }
+
+       public static class B01 {
+               public int f1;
+       }
+
+       @Test
+       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));
+               assertTrue(empty((ExternalDocs)null));
+               assertTrue(empty((License)null));
+               assertTrue(empty((Schema)null));
+               assertTrue(empty((Items)null));
+               assertTrue(empty((SubItems)null));
+
+               Field f = B01.class.getField("f1");
+               assertTrue(empty(f.getAnnotation(Query.class)));
+               assertTrue(empty(f.getAnnotation(Header.class)));
+               assertTrue(empty(f.getAnnotation(FormData.class)));
+               assertTrue(empty(f.getAnnotation(Path.class)));
+       }
+
+       @Test
+       public void testAllEmpty1() {
+               assertTrue(allEmpty(new String[0]));
+               assertTrue(allEmpty(""));
+               assertTrue(allEmpty(null,""));
+               assertFalse(allEmpty(null,"","x"));
+       }
+
+       @Test
+       public void testAllEmpty2() {
+               assertTrue(allEmpty(new String[0],new String[0]));
+               assertTrue(allEmpty(null,new String[0]));
+               assertTrue(allEmpty(null,new String[]{""}));
+               assertFalse(allEmpty(null,new String[]{"x"}));
+       }
+
+       @Test
+       public void testAllFalse() {
+               assertTrue(allFalse());
+               assertTrue(allFalse(false));
+               assertTrue(allFalse(false,false));
+               assertFalse(allFalse(false,true));
+               assertFalse(allFalse(true));
+       }
+
+       @Test
+       public void testAllMinusOne() {
+               assertTrue(allMinusOne());
+               assertTrue(allMinusOne(-1));
+               assertTrue(allMinusOne(-1,-1));
+               assertFalse(allMinusOne(-1,0));
+               assertFalse(allMinusOne(0));
+       }
+
+       @Test
+       public void testAllMinusOneLongs() {
+               assertTrue(allMinusOne(-1l));
+               assertTrue(allMinusOne(-1l,-1l));
+               assertFalse(allMinusOne(-1l,0l));
+               assertFalse(allMinusOne(0l));
+       }
+
+       @Body public static class C01a {}
+       @Body(usePartParser=true) public static class C01b {}
+       @Body(schema=@Schema) public static class C01c {}
+       @Body(schema=@Schema(description="foo")) public static class C01d {}
+       @Body(partParser=OpenApiPartParser.class) public static class C01e {}
+
+       @Test
+       public void usePartParserBody() {
+               
assertFalse(usePartParser(C01a.class.getAnnotation(Body.class)));
+               assertTrue(usePartParser(C01b.class.getAnnotation(Body.class)));
+               
assertFalse(usePartParser(C01c.class.getAnnotation(Body.class)));
+               assertTrue(usePartParser(C01d.class.getAnnotation(Body.class)));
+               assertTrue(usePartParser(C01e.class.getAnnotation(Body.class)));
+       }
+
+       @Body public static class D01a {}
+       @Body(usePartSerializer=true) public static class D01b {}
+       @Body(schema=@Schema) public static class D01c {}
+       @Body(schema=@Schema(description="foo")) public static class D01d {}
+       @Body(partSerializer=OpenApiPartSerializer.class) public static class 
D01e {}
+
+       @Test
+       public void usePartSerializerBody() {
+               
assertFalse(usePartSerializer(D01a.class.getAnnotation(Body.class)));
+               
assertTrue(usePartSerializer(D01b.class.getAnnotation(Body.class)));
+               
assertFalse(usePartSerializer(D01c.class.getAnnotation(Body.class)));
+               
assertTrue(usePartSerializer(D01d.class.getAnnotation(Body.class)));
+               
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 {}
+       @Response(schema=@Schema(description="foo")) public static class F01d {}
+       @Response(partSerializer=OpenApiPartSerializer.class) public static 
class F01e {}
+
+       @Test
+       public void usePartSerializerResponse() {
+               
assertFalse(usePartSerializer(F01a.class.getAnnotation(Response.class)));
+               
assertTrue(usePartSerializer(F01b.class.getAnnotation(Response.class)));
+               
assertFalse(usePartSerializer(F01c.class.getAnnotation(Response.class)));
+               
assertTrue(usePartSerializer(F01d.class.getAnnotation(Response.class)));
+               
assertTrue(usePartSerializer(F01e.class.getAnnotation(Response.class)));
+       }
+}
\ No newline at end of file
diff --git 
a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
 
b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
index f8ab27d..e4d2943 100644
--- 
a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
+++ 
b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
@@ -614,6 +614,48 @@ public class Swagger extends SwaggerElement {
        }
 
        /**
+        * Shortcut for calling <code>getPaths().get(path);</code>
+        *
+        * @param path The path (e.g. <js>"/foo"</js>).
+        * @return The operation map for the specified path, or <jk>null</jk> 
if it doesn't exist.
+        */
+       public OperationMap getPath(String path) {
+               return getPaths().get(path);
+       }
+
+       /**
+        * Shortcut for calling 
<code>getPaths().get(path).get(operation);</code>
+        *
+        * @param path The path (e.g. <js>"/foo"</js>).
+        * @param operation The HTTP operation (e.g. <js>"get"</js>).
+        * @return The operation for the specified path and operation id, or 
<jk>null</jk> if it doesn't exist.
+        */
+       public Operation getOperation(String path, String operation) {
+               OperationMap om = getPath(path);
+               if (om == null)
+                       return null;
+               return om.get(operation);
+       }
+
+       /**
+        * Shortcut for calling 
<code>getPaths().get(path).get(operation).getResponse(status);</code>
+        *
+        * @param path The path (e.g. <js>"/foo"</js>).
+        * @param operation The HTTP operation (e.g. <js>"get"</js>).
+        * @param status The HTTP response status (e.g. <js>"200"</js>).
+        * @return The operation for the specified path and operation id, or 
<jk>null</jk> if it doesn't exist.
+        */
+       public ResponseInfo getResponseInfo(String path, String operation, 
Object status) {
+               OperationMap om = getPath(path);
+               if (om == null)
+                       return null;
+               Operation op = om.get(operation);
+               if (op == null)
+                       return null;
+               return op.getResponse(status);
+       }
+
+       /**
         * Bean property setter:  <property>paths</property>.
         *
         * <p>
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 784c374..ff363c5 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -76,7 +76,6 @@ public class BeanSession extends Session {
        }
 
        /**
-       /**
         * Converts the specified value to the specified class type.
         *
         * <p>
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Value.java 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Value.java
index 45752f0..aefab7c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Value.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Value.java
@@ -44,7 +44,7 @@ public class Value<T> {
         * @param i The index of the parameter.
         * @return The parameter type of the value, or <jk>null</jk> if the 
method parameter is not of type <code>Value</code>.
         */
-       public static Class<?> getParameterType(Method m, int i) {
+       public static Type getParameterType(Method m, int i) {
                return getParameterType(m.getGenericParameterTypes()[i]);
        }
 
@@ -54,17 +54,13 @@ public class Value<T> {
         * @param t The type to find the parameter type of.
         * @return The parameter type of the value, or <jk>null</jk> if the 
type is not a subclass of <code>Value</code>.
         */
-       public static Class<?> getParameterType(Type t) {
+       public static Type getParameterType(Type t) {
                if (t instanceof ParameterizedType) {
                        ParameterizedType pt = (ParameterizedType)t;
                        if (pt.getRawType() == Value.class) {
                                Type[] ta = pt.getActualTypeArguments();
-                               if (ta.length > 0) {
-                                       Type t2 = ta[0];
-                                       if (t2 instanceof Class) {
-                                               return (Class<?>)t2;
-                                       }
-                               }
+                               if (ta.length > 0) 
+                                       return ta[0];
                        }
                } else if (t instanceof Class) {
                        Class<?> c = (Class<?>)t;
@@ -83,8 +79,8 @@ public class Value<T> {
         * @return <jk>true</jk> if the specified type is this class.
         */
        public static boolean isType(Type t) {
-               return 
-                       (t instanceof ParameterizedType && 
((ParameterizedType)t).getRawType() == Value.class) 
+               return
+                       (t instanceof ParameterizedType && 
((ParameterizedType)t).getRawType() == Value.class)
                        || (t instanceof Class && 
Value.class.isAssignableFrom((Class<?>)t));
        }
 
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 5044fc3..f22c67a 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
@@ -12,6 +12,8 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
+import org.apache.juneau.httppart.*;
+
 /**
  * Various reusable utility methods when working with annotations.
  */
@@ -253,9 +255,10 @@ public class AnnotationUtils {
         * @return <jk>true</jk> if all the specified strings are empty or null.
         */
        protected static boolean allEmpty(String...strings) {
-               for (String s : strings)
-                       if (s != null && ! s.isEmpty())
-                               return false;
+               if (strings != null)
+                       for (String s : strings)
+                               if (s != null && ! s.isEmpty())
+                                       return false;
                return true;
        }
 
@@ -267,7 +270,7 @@ public class AnnotationUtils {
         */
        protected static boolean allEmpty(String[]...strings) {
                for (String[] s : strings)
-                       if (s.length != 0 || ! allEmpty(s))
+                       if (s != null && s.length > 0 && ! allEmpty(s))
                                return false;
                return true;
        }
@@ -297,4 +300,56 @@ public class AnnotationUtils {
                                return false;
                return true;
        }
+
+       /**
+        * Returns <jk>true</jk> if the part parser should be used on the 
specified part.
+        *
+        * @param a The annotation to check.
+        * @return <jk>true</jk> if the part parser should be used on the 
specified part.
+        */
+       public static boolean usePartParser(Body a) {
+               return
+                       a.usePartParser()
+                       || a.partParser() != HttpPartParser.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(Body 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(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()
+                       || a.partSerializer() != HttpPartSerializer.Null.class
+                       || ! empty(a.schema());
+       }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Contact.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Contact.java
index fc528be..f952779 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Contact.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Contact.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
-import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
@@ -60,9 +59,7 @@ import java.lang.annotation.*;
  * </ul>
  */
 @Documented
-@Target({PARAMETER,TYPE})
 @Retention(RUNTIME)
-@Inherited
 public @interface Contact {
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ExternalDocs.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ExternalDocs.java
index 73a3c57..92b53fe 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ExternalDocs.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ExternalDocs.java
@@ -12,6 +12,10 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
 /**
  * Swagger external documentation annotation.
  *
@@ -51,6 +55,8 @@ package org.apache.juneau.http.annotation;
  *     <li class='link'><a class="doclink" 
href="https://swagger.io/specification/v2/#externalDocumentationObject";>Swagger 
Specification &gt; External Documentation Object</a>
  * </ul>
  */
+@Documented
+@Retention(RUNTIME)
 public @interface ExternalDocs {
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Items.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Items.java
index 5d0d886..291b6e7 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Items.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Items.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
-import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
@@ -80,9 +79,7 @@ import org.apache.juneau.httppart.*;
  * </ul>
  */
 @Documented
-@Target({PARAMETER,TYPE})
 @Retention(RUNTIME)
-@Inherited
 public @interface Items {
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/License.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/License.java
index fad1959..764e8dd 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/License.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/License.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
-import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
@@ -57,9 +56,7 @@ import java.lang.annotation.*;
  * </ul>
  */
 @Documented
-@Target({PARAMETER,TYPE})
 @Retention(RUNTIME)
-@Inherited
 public @interface License {
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RBody.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RBody.java
deleted file mode 100644
index ebe9069..0000000
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RBody.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// 
***************************************************************************************************************************
-// * 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.http.annotation;
-
-/**
- * TODO
- * 
- */
-public class RBody {
-
-}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RStatus.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RStatus.java
deleted file mode 100644
index 566e9e6..0000000
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/RStatus.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// 
***************************************************************************************************************************
-// * 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.http.annotation;
-
-/**
- * TODO
- *
- */
-public class RStatus {
-}
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 20bd1ec..1ba720d 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({TYPE})
+@Target({PARAMETER,TYPE})
 @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 271cdfb..e1aafcb 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
@@ -27,7 +27,7 @@ import org.apache.juneau.jsonschema.*;
  * TODO
  */
 @Documented
-@Target({PARAMETER,TYPE,METHOD})
+@Target({PARAMETER,METHOD,TYPE})
 @Retention(RUNTIME)
 @Inherited
 public @interface ResponseBody {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseHeader.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseHeader.java
index 7d2992c..fc8527a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseHeader.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/ResponseHeader.java
@@ -82,7 +82,7 @@ import org.apache.juneau.httppart.*;
  * </ul>
 */
 @Documented
-@Target({PARAMETER,TYPE,METHOD})
+@Target({PARAMETER,METHOD,TYPE})
 @Retention(RUNTIME)
 @Inherited
 public @interface ResponseHeader {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Schema.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Schema.java
index 4eebb27..222ca28 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Schema.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Schema.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
-import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
@@ -75,9 +74,7 @@ import org.apache.juneau.httppart.*;
  * </ul>
  */
 @Documented
-@Target({PARAMETER,TYPE})
 @Retention(RUNTIME)
-@Inherited
 public @interface Schema {
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/SubItems.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/SubItems.java
index a2b200d..c55352e 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/SubItems.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/SubItems.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
-import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
@@ -34,9 +33,7 @@ import java.lang.annotation.*;
  * </ul>
  */
 @Documented
-@Target({PARAMETER,TYPE})
 @Retention(RUNTIME)
-@Inherited
 public @interface SubItems {
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Tag.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Tag.java
index 304cb32..7d8cd6f 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Tag.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Tag.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.annotation;
 
-import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
@@ -62,9 +61,7 @@ import java.lang.annotation.*;
  * </ul>
  */
 @Documented
-@Target({PARAMETER,TYPE})
 @Retention(RUNTIME)
-@Inherited
 public @interface Tag {
 
        /**
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 906a170..3ecef7e 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
@@ -128,15 +128,15 @@ public class HttpPartSchemaBuilder {
                allowEmptyValue(! a.required());
                serializer(a.partSerializer());
                parser(a.partParser());
-               usePartSerializer(a.usePartSerializer() || a.partSerializer() 
!= HttpPartSerializer.Null.class);
-               usePartParser(a.usePartParser() || a.partParser() != 
HttpPartParser.Null.class);
+               usePartSerializer(AnnotationUtils.usePartSerializer(a));
+               usePartParser(AnnotationUtils.usePartParser(a));
                apply(a.schema());
                return this;
        }
 
        HttpPartSchemaBuilder apply(ResponseBody a) {
                serializer(a.partSerializer());
-               usePartSerializer(a.usePartSerializer() || a.partSerializer() 
!= HttpPartSerializer.Null.class);
+               usePartSerializer(AnnotationUtils.usePartSerializer(a));
                apply(a.schema());
                return this;
        }
@@ -284,7 +284,7 @@ public class HttpPartSchemaBuilder {
                required(false);
                allowEmptyValue(true);
                serializer(a.partSerializer());
-               usePartSerializer(a.usePartSerializer() || a.partSerializer() 
!= HttpPartSerializer.Null.class);
+               usePartSerializer(AnnotationUtils.usePartSerializer(a));
                apply(a.schema());
                return this;
        }
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 f5d1db8..c61c5ab 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
@@ -2157,6 +2157,36 @@ public final class ClassUtils {
        }
 
        /**
+        * Returns the simple name of a class.
+        *
+        * <p>
+        * Similar to {@link Class#getSimpleName()}, but includes the simple 
name of an enclosing or declaring class.
+        *
+        * @param t The class to get the simple name on.
+        * @return The simple name of a class.
+        */
+       public static String getSimpleName(Type t) {
+               if (t instanceof Class)
+                       return getSimpleName((Class<?>)t);
+               if (t instanceof ParameterizedType) {
+                       StringBuilder sb = new StringBuilder();
+                       ParameterizedType pt = (ParameterizedType)t;
+                       sb.append(getSimpleName(pt.getRawType()));
+                       sb.append("<");
+                       boolean first = true;
+                       for (Type t2 : pt.getActualTypeArguments()) {
+                               if (! first)
+                                       sb.append(',');
+                               first = false;
+                               sb.append(getSimpleName(t2));
+                       }
+                       sb.append(">");
+                       return sb.toString();
+               }
+               return null;
+       }
+
+       /**
         * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Method, 
int)} returns a value.
         *
         * @param a The annotation to check for.
@@ -2180,14 +2210,14 @@ public final class ClassUtils {
        }
 
        /**
-        * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Class)} 
returns a value.
+        * Returns <jk>true</jk> if the {@link #getAnnotation(Class, Type)} 
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.
+        * @param t The class to check.
+        * @return <jk>true</jk> if the {@link #getAnnotation(Class, Type)} 
returns a value.
         */
-       public static boolean hasAnnotation(Class<? extends Annotation> a, 
Class<?> c) {
-               return getAnnotation(a, c) != null;
+       public static boolean hasAnnotation(Class<? extends Annotation> a, Type 
t) {
+               return getAnnotation(a, t) != null;
        }
 
        /**
@@ -2206,9 +2236,7 @@ public final class ClassUtils {
                Type t = m.getGenericParameterTypes()[index];
                if (Value.isType(t))
                        return getAnnotation(a, Value.getParameterType(t));
-               if (t instanceof Class)
-                       return getAnnotation(a, (Class<?>)t);
-               return null;
+               return getAnnotation(a, t);
        }
 
        /**
@@ -2229,10 +2257,10 @@ public final class ClassUtils {
                        if (a.isInstance(a2))
                                l.add((T)a2);
                Type t = m.getGenericParameterTypes()[index];
-               if (t instanceof Class)
-                       appendAnnotations(a, (Class<?>)t, l);
-               else if (Value.isType(t))
+               if (Value.isType(t))
                        appendAnnotations(a, Value.getParameterType(t), l);
+               else
+                       appendAnnotations(a, t, l);
                return l;
        }
 
@@ -2270,10 +2298,10 @@ public final class ClassUtils {
                        if (a.isInstance(a2))
                                l.add((T)a2);
                Type t = m.getGenericReturnType();
-               if (t instanceof Class)
-                       appendAnnotations(a, (Class<?>)t, l);
-               else if (Value.isType(t))
+               if (Value.isType(t))
                        appendAnnotations(a, Value.getParameterType(t), l);
+               else
+                       appendAnnotations(a, t, l);
                return l;
        }
 
@@ -2305,10 +2333,7 @@ public final class ClassUtils {
                for (Annotation a2 :  m.getAnnotations())
                        if (a.isInstance(a2))
                                return (T)a2;
-               Type t = m.getGenericReturnType();
-               if (t instanceof Class)
-                       return getAnnotation(a, (Class<?>)t);
-               return null;
+               return getAnnotation(a, m.getGenericReturnType());
        }
 
        /**
@@ -2316,25 +2341,25 @@ public final class ClassUtils {
         *
         * @param <T> The annotation class type.
         * @param a The annotation class.
-        * @param c The annotated class.
+        * @param t The annotated class.
         * @return The annotation, or <jk>null</jk> if not found.
         */
-       public static <T extends Annotation> T getAnnotation(Class<T> a, 
Class<?> c) {
-               if (c == null)
-                       return null;
-
-               T t = getDeclaredAnnotation(a, c);
-               if (t != null)
-                       return t;
-
-               t = getAnnotation(a, c.getSuperclass());
-               if (t != null)
-                       return t;
-
-               for (Class<?> c2 : c.getInterfaces()) {
-                       t = getAnnotation(a, c2);
-                       if (t != null)
-                               return t;
+       public static <T extends Annotation> T getAnnotation(Class<T> a, Type 
t) {
+               Class<?> c = toClass(t);
+               if (c != null) {
+                       T t2 = getDeclaredAnnotation(a, c);
+                       if (t2 != null)
+                               return t2;
+
+                       t2 = getAnnotation(a, c.getSuperclass());
+                       if (t2 != null)
+                               return t2;
+
+                       for (Class<?> c2 : c.getInterfaces()) {
+                               t2 = getAnnotation(a, c2);
+                               if (t2 != null)
+                                       return t2;
+                       }
                }
                return null;
        }
@@ -2348,14 +2373,16 @@ public final class ClassUtils {
         *
         * @param <T> The annotation class type.
         * @param a The annotation class.
-        * @param c The annotated class.
+        * @param t The annotated class.
         * @return The annotation, or <jk>null</jk> if not found.
         */
        @SuppressWarnings("unchecked")
-       public static <T extends Annotation> T getDeclaredAnnotation(Class<T> 
a, Class<?> c) {
-               for (Annotation a2 : c.getDeclaredAnnotations())
-                       if (a2.annotationType() == a)
-                               return (T)a2;
+       public static <T extends Annotation> T getDeclaredAnnotation(Class<T> 
a, Type t) {
+               Class<?> c = toClass(t);
+               if (c != null)
+                       for (Annotation a2 : c.getDeclaredAnnotations())
+                               if (a2.annotationType() == a)
+                                       return (T)a2;
                return null;
        }
 
@@ -2368,30 +2395,30 @@ public final class ClassUtils {
         *
         * @param <T> The annotation class type.
         * @param a The annotation class type.
-        * @param c The class being searched.
+        * @param t The class being searched.
         * @return The found matches, or an empty array if annotation was not 
found.
         */
-       public static <T extends Annotation> List<T> getAnnotations(Class<T> a, 
Class<?> c) {
+       public static <T extends Annotation> List<T> getAnnotations(Class<T> a, 
Type t) {
                List<T> l = new LinkedList<>();
-               appendAnnotations(a, c, l);
+               appendAnnotations(a, t, l);
                return l;
        }
 
        /**
-        * Same as {@link #getAnnotations(Class, Class)} but returns the list 
in parent-to-child order.
+        * Same as {@link #getAnnotations(Class, Type)} but returns the list in 
parent-to-child order.
         *
         * @param a The annotation class type.
-        * @param c The class being searched.
+        * @param t The class being searched.
         * @return The found matches, or an empty array if annotation was not 
found.
         */
-       public static <T extends Annotation> List<T> 
getAnnotationsParentFirst(Class<T> a, Class<?> c) {
-               List<T> l = getAnnotations(a, c);
+       public static <T extends Annotation> List<T> 
getAnnotationsParentFirst(Class<T> a, Type t) {
+               List<T> l = getAnnotations(a, t);
                Collections.reverse(l);
                return l;
        }
 
        /**
-        * Same as {@link #getAnnotations(Class, Class)} except returns the 
annotations as a map with the keys being the
+        * Same as {@link #getAnnotations(Class, Type)} except returns the 
annotations as a map with the keys being the
         * class on which the annotation was found.
         *
         * <p>
@@ -2399,39 +2426,40 @@ public final class ClassUtils {
         *
         * @param <T> The annotation class type.
         * @param a The annotation class type.
-        * @param c The class being searched.
+        * @param t The class being searched.
         * @return The found matches, or an empty map if annotation was not 
found.
         */
-       public static <T extends Annotation> LinkedHashMap<Class<?>,T> 
getAnnotationsMap(Class<T> a, Class<?> c) {
+       public static <T extends Annotation> LinkedHashMap<Class<?>,T> 
getAnnotationsMap(Class<T> a, Type t) {
                LinkedHashMap<Class<?>,T> m = new LinkedHashMap<>();
-               findAnnotationsMap(a, c, m);
+               findAnnotationsMap(a, t, m);
                return m;
        }
 
        /**
-        * Same as {@link #getAnnotationsMap(Class, Class)} except returns 
results in parent-to-child order.
+        * Same as {@link #getAnnotationsMap(Class, Type)} except returns 
results in parent-to-child order.
         *
         * @param <T> The annotation class type.
         * @param a The annotation class type.
-        * @param c The class being searched.
+        * @param t The class being searched.
         * @return The found matches, or an empty map if annotation was not 
found.
         */
-       public static <T extends Annotation> LinkedHashMap<Class<?>,T> 
getAnnotationsMapParentFirst(Class<T> a, Class<?> c) {
-               return CollectionUtils.reverse(getAnnotationsMap(a, c));
+       public static <T extends Annotation> LinkedHashMap<Class<?>,T> 
getAnnotationsMapParentFirst(Class<T> a, Type t) {
+               return CollectionUtils.reverse(getAnnotationsMap(a, t));
        }
 
-       private static <T extends Annotation> void findAnnotationsMap(Class<T> 
a, Class<?> c, Map<Class<?>,T> m) {
-               if (c == null)
-                       return;
+       private static <T extends Annotation> void findAnnotationsMap(Class<T> 
a, Type t, Map<Class<?>,T> m) {
+               Class<?> c = toClass(t);
+               if (c != null) {
 
-               T t = getDeclaredAnnotation(a, c);
-               if (t != null)
-                       m.put(c, t);
+                       T t2 = getDeclaredAnnotation(a, c);
+                       if (t2 != null)
+                               m.put(c, t2);
 
-               findAnnotationsMap(a, c.getSuperclass(), m);
+                       findAnnotationsMap(a, c.getSuperclass(), m);
 
-               for (Class<?> c2 : c.getInterfaces())
-                       findAnnotationsMap(a, c2, m);
+                       for (Class<?> c2 : c.getInterfaces())
+                               findAnnotationsMap(a, c2, m);
+               }
        }
 
        /**
@@ -2439,22 +2467,33 @@ public final class ClassUtils {
         * list.
         *
         * @param a The annotation.
-        * @param c The class.
+        * @param t The class.
         * @param l The list of annotations.
         */
-       public static <T extends Annotation> void appendAnnotations(Class<T> a, 
Class<?> c, List<T> l) {
-               if (c == null)
-                       return;
+       public static <T extends Annotation> void appendAnnotations(Class<T> a, 
Type t, List<T> l) {
+               Class<?> c = toClass(t);
+               if (c != null) {
+                       addIfNotNull(l, getDeclaredAnnotation(a, c));
 
-               addIfNotNull(l, getDeclaredAnnotation(a, c));
+                       if (c.getPackage() != null)
+                               addIfNotNull(l, 
c.getPackage().getAnnotation(a));
 
-               if (c.getPackage() != null)
-                       addIfNotNull(l, c.getPackage().getAnnotation(a));
+                       appendAnnotations(a, c.getSuperclass(), l);
 
-               appendAnnotations(a, c.getSuperclass(), l);
+                       for (Class<?> c2 : c.getInterfaces())
+                               appendAnnotations(a, c2, l);
+               }
+       }
 
-               for (Class<?> c2 : c.getInterfaces())
-                       appendAnnotations(a, c2, l);
+       private static Class<?> toClass(Type t) {
+               if (t instanceof Class)
+                       return (Class<?>)t;
+               if (t instanceof ParameterizedType) {
+                       ParameterizedType pt = (ParameterizedType)t;
+                       // The raw type should always be a class (right?)
+                       return (Class<?>)pt.getRawType();
+               }
+               return null;
        }
 
        /**
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 455ed03..b156911 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -313,7 +313,7 @@
                        <li><p class='new'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.Header'>@Header</a></p>
                        <li><p class='new'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.Path'>@Path</a></p>
                        <li><p class='new'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.RequestBean'>@RequestBean</a></p>
-                       <li><p class='todo'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.ResponseBody'>@ResponseBody</a></p>
+                       <li><p class='new'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.ResponseBody'>@ResponseBody</a></p>
                        <li><p class='todo'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.ResponseHeader'>@ResponseHeader</a></p>
                        <li><p class='todo'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.ResponseStatus'>@ResponseStatus</a></p>
                        <li><p class='todo'><a class='doclink' 
href='#juneau-rest-server.HttpPartAnnotations.Response'>@Response</a></p>
@@ -15615,28 +15615,42 @@ TODO(7.2.0)
 <h4 class='topic new' onclick='toggle(this)'><a 
href='#juneau-rest-server.HttpPartAnnotations.RequestBean' 
id='juneau-rest-server.HttpPartAnnotations.RequestBean'>7.9.8 - 
@RequestBean</a></h4>
 <div class='topic'><!-- START: 7.9.8 - 
juneau-rest-server.HttpPartAnnotations.RequestBean -->
 <p>
-       The {@link org.apache.juneau.http.annotation.RequestBean @RequestBean} 
annotation can be applied to a parameter or parameter type of a 
<ja>@RestMethod</ja>-annotated method 
+       The {@link org.apache.juneau.http.annotation.RequestBean @RequestBean} 
annotation can be applied to a parameter interface type of a 
<ja>@RestMethod</ja>-annotated method 
        to identify it as an interface for retrieving HTTP parts through a bean 
interface.
 </p>
 
 <h5 class='section'>Example:</h5>
 <p class='bpcode w800'>
-       <ja>@RestMethod</ja>(path=<js>"/mypath/{p1}/{p2}/*"</js>)
-       <jk>public void</jk> myMethod(<ja>@RequestBean</ja> MyRequestBean rb) 
{...}
+       <ja>@RestMethod</ja>(path=<js>"/pets/{petId}"</js>)
+       <jk>public void</jk> putPet(UpdatePet updatePet) {...}
 
-       <jk>public interface</jk> MyRequestBean {
+       <ja>@RequestBean</ja>
+       <jk>public interface</jk> UpdatePet {
 
-               <ja>@Path</ja> <jc>// Path variable name inferred from 
getter.</jc>
-               String getP1();
+               <ja>@Path</ja> 
+               <jk>int</jk> getPetId();
 
-               <ja>@Path</ja>(<js>"p2"</js>)
-               String getX();
+               <ja>@Query</ja>(name=<js>"verbose"</js>)
+               <jk>boolean</jk> isDebug();
 
-               <ja>@Path</ja>(<js>"/*"</js>)
-               String getRemainder();
+               <ja>@Header</ja>(<js>"*"</js>)
+               Map&lt;String,Object&gt; getAllHeaders();
 
-               <ja>@Query</ja>
-               String getQ1();
+               <ja>@Body</ja>
+               Pet getPet();
+</p>
+<p>
+       The return types of the getters must be the supported parameter types 
for the HTTP-part annotation used.
+       <br>Schema-based serialization and parsing is used just as if used as 
individual parameter types.
+</p>
+<p>
+       It should be noted that the annotations used are the exact same used on 
REST parameters and have all the
+       same feature support including automatic Swagger validation and 
documentation.  
+</p>
+<h5 class='figure'>Example:</h5>
+<p class='bpcode w800'>
+       <ja>@RequestBean</ja>
+       <jk>public interface</jk> RequestBean {
 
                <jc>// Schema-based query parameter:  Pipe-delimited lists of 
comma-delimited lists of integers.</jc>
                <ja>@Query</ja>(
@@ -15644,35 +15658,77 @@ TODO(7.2.0)
                        items=<ja>@Items</ja>(
                                items=<ja>@SubItems</ja>(
                                        collectionFormat=<js>"csv"</js>
-                                       type=<js>"integer"</js>
-                               )
+                                       type=<js>"integer"</js>,
+                                       minimum=1,
+                                       maximum=100
+                               ),
+                               maximumLength=10
                        )
                )
-               <jk>int</jk>[][] getQ3();
-
-               <ja>@Header</ja>(<js>"*"</js>)
-               Map&lt;String,Object&gt; getHeaders();
-</p>
-<p class='bpcode w800'>
-       <jc>// Same as above but annotation defined on interface.</jc>
-       <ja>@RestMethod</ja>(path=<js>"/mypath/{p1}/{p2}/*"</js>)
-       <jk>public void</jk> myMethod(MyRequestBean rb) {...}
-
-       <ja>@RequestBean</ja>
-       <jk>public interface</jk> MyRequestBean {...}
-</p>
-<p>
-       The return types of the getters must be the supported parameter types 
for the HTTP-part annotation used.
-       <br>Schema-based serialization and parsing is used just as if used as 
individual parameter types.
+               <jk>int</jk>[][] getPipeCdlInts();
+       }
 </p>
 </div><!-- END: 7.9.8 - juneau-rest-server.HttpPartAnnotations.RequestBean -->
 
 <!-- 
====================================================================================================
 -->
 
-<h4 class='topic todo' onclick='toggle(this)'><a 
href='#juneau-rest-server.HttpPartAnnotations.ResponseBody' 
id='juneau-rest-server.HttpPartAnnotations.ResponseBody'>7.9.9 - 
@ResponseBody</a></h4>
+<h4 class='topic new' onclick='toggle(this)'><a 
href='#juneau-rest-server.HttpPartAnnotations.ResponseBody' 
id='juneau-rest-server.HttpPartAnnotations.ResponseBody'>7.9.9 - 
@ResponseBody</a></h4>
 <div class='topic'><!-- START: 7.9.9 - 
juneau-rest-server.HttpPartAnnotations.ResponseBody -->
 <p>
+       The {@link org.apache.juneau.http.annotation.ResponseBody 
@ResponseBody} annotation is used to identify a placeholder parameter
+       for an HTTP response.
 </p>
+<ul class='doctree'>
+       <li class='ja'>{@link org.apache.juneau.http.annotation.ResponseBody 
ResponseBody}
+       <ul>
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#api() api()} - Free-form Swagger 
JSON.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#example() example()} - 
Serialized example.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#examples() examples()} - 
Serialized examples per media type.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#partSerializer() 
partSerializer()} - Override the part serializer.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#schema() schema()} - Swagger 
schema.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#usePartSerializer() 
usePartSerializer()} - Use the HTTP-Part serializer for serializing the body.
+       </ul>
+</ul>
+<p>
+       It can be used in the following locations:
+</p>
+<ul>
+       <li>Parameters on <ja>@RestMethod</ja>-annotated methods.
+       <li>Classes of parameters on <ja>@RestMethod</ja>-annotated methods.
+       <li><ja>@RestMethod</ja>-annotated methods.
+</ul>
+
+<h5 class='topic'>Parameters on @RestMethod-annotated methods</h5>
+<p>
+       When used on a method parameter, this annotation is used to identify a 
placeholder {@link org.apache.juneau.Value}
+       object that can be used to return the body of the response.
+</p>
+<p class='bpcode w800'>
+       <ja>@RestMethod</ja>(path="/pet/{petId}")
+       <jk>public void</jk> getPet(<ja>@Path</ja>(<js>"petId"</js>) 
<jk>int</jk> id, <ja>@ResponseBody</ja> Value&lt;Pet&gt; body) {
+               body.set(<jsm>getPet</jsm>(id));
+       }
+</p>
+<p>
+       The behavior is identical to the following:
+</p>
+<p class='bpcode w800'>
+       <ja>@RestMethod</ja>(path="/pet/{petId}")
+       <jk>public</jk> Pet getPet(<ja>@Path</ja>(<js>"petId"</js>) 
<jk>int</jk> id) {
+               <jk>return</jk> <jsm>getPet</jsm>(id);
+       }
+</p>
+<p>
+       You may ask why you would use ever use this.
+       The answer is that it's possible to have your method returns something 
else such as an HTTP response status
+       or header value.
+       This annotation allows you to set a response body when your method is 
returning one of these.
+</p>
+
+<h5 class='topic'>Classes of parameters on @RestMethod-annotated methods</h5>
+
+
+<h5 class='topic'>@RestMethod-annotated methods</h5>
 </div><!-- END: 7.9.9 - juneau-rest-server.HttpPartAnnotations.ResponseBody -->
 
 <!-- 
====================================================================================================
 -->
diff --git 
a/juneau-doc/src/main/resources/Topics/02.juneau-marshall/10.Transforms/02.PerMediaTypePojoSwaps.html
 
b/juneau-doc/src/main/resources/Topics/02.juneau-marshall/10.Transforms/02.PerMediaTypePojoSwaps.html
index f0806b1..d0d071e 100644
--- 
a/juneau-doc/src/main/resources/Topics/02.juneau-marshall/10.Transforms/02.PerMediaTypePojoSwaps.html
+++ 
b/juneau-doc/src/main/resources/Topics/02.juneau-marshall/10.Transforms/02.PerMediaTypePojoSwaps.html
@@ -13,7 +13,7 @@
  
***************************************************************************************************************************/
  -->
 
-Per-media-type PojoSwaps
+{new} Per-media-type PojoSwaps
 
 <p>
        Swaps can also be defined per-media-type.               
@@ -89,3 +89,23 @@ Per-media-type PojoSwaps
                }
        }
 </p>
+<p>
+       When multiple swaps match the same media type, a best-match algorithm 
is applied to find the correct
+       swap to use.
+</p>
+<p>
+       In later sections we describe how annotations can be used to shorten 
this syntax:
+</p>
+<p class='bpcode w800'>
+       
<ja>@Swaps</ja>({MyJsonSwap.<jk>class</jk>,MyXmlSwap.<jk>class</jk>,MyOtherSwap.<jk>class</jk>})
+       <jk>public static class</jk> MyPojo {}
+
+       <ja>@Swap</ja>(mediaTypes=<js>"&#42;/json"</js>)
+       <jk>public static class</jk> MyJsonSwap <jk>extends</jk> 
PojoSwap&lt;MyPojo,String&gt; {...}
+
+       <ja>@Swap</ja>(mediaTypes=<js>"&#42;/xml"</js>)
+       <jk>public static class</jk> MyXmlSwap <jk>extends</jk> 
PojoSwap&lt;MyPojo,String&gt; {...}
+
+       <ja>@Swap</ja>(mediaTypes=<js>"&#42;/*"</js>)
+       <jk>public static class</jk> MyOtherSwap <jk>extends</jk> 
PojoSwap&lt;MyPojo,String&gt; {...}
+</p>>
\ No newline at end of file
diff --git 
a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/08.RequestBean.html
 
b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/08.RequestBean.html
index 5bd004d..91c9ed7 100644
--- 
a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/08.RequestBean.html
+++ 
b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/08.RequestBean.html
@@ -16,28 +16,42 @@
 {new} @RequestBean
 
 <p>
-       The {@link org.apache.juneau.http.annotation.RequestBean @RequestBean} 
annotation can be applied to a parameter or parameter type of a 
<ja>@RestMethod</ja>-annotated method 
+       The {@link org.apache.juneau.http.annotation.RequestBean @RequestBean} 
annotation can be applied to a parameter interface type of a 
<ja>@RestMethod</ja>-annotated method 
        to identify it as an interface for retrieving HTTP parts through a bean 
interface.
 </p>
 
 <h5 class='section'>Example:</h5>
 <p class='bpcode w800'>
-       <ja>@RestMethod</ja>(path=<js>"/mypath/{p1}/{p2}/*"</js>)
-       <jk>public void</jk> myMethod(<ja>@RequestBean</ja> MyRequestBean rb) 
{...}
+       <ja>@RestMethod</ja>(path=<js>"/pets/{petId}"</js>)
+       <jk>public void</jk> putPet(UpdatePet updatePet) {...}
 
-       <jk>public interface</jk> MyRequestBean {
+       <ja>@RequestBean</ja>
+       <jk>public interface</jk> UpdatePet {
 
-               <ja>@Path</ja> <jc>// Path variable name inferred from 
getter.</jc>
-               String getP1();
+               <ja>@Path</ja> 
+               <jk>int</jk> getPetId();
 
-               <ja>@Path</ja>(<js>"p2"</js>)
-               String getX();
+               <ja>@Query</ja>(name=<js>"verbose"</js>)
+               <jk>boolean</jk> isDebug();
 
-               <ja>@Path</ja>(<js>"/*"</js>)
-               String getRemainder();
+               <ja>@Header</ja>(<js>"*"</js>)
+               Map&lt;String,Object&gt; getAllHeaders();
 
-               <ja>@Query</ja>
-               String getQ1();
+               <ja>@Body</ja>
+               Pet getPet();
+</p>
+<p>
+       The return types of the getters must be the supported parameter types 
for the HTTP-part annotation used.
+       <br>Schema-based serialization and parsing is used just as if used as 
individual parameter types.
+</p>
+<p>
+       It should be noted that the annotations used are the exact same used on 
REST parameters and have all the
+       same feature support including automatic Swagger validation and 
documentation.  
+</p>
+<h5 class='figure'>Example:</h5>
+<p class='bpcode w800'>
+       <ja>@RequestBean</ja>
+       <jk>public interface</jk> RequestBean {
 
                <jc>// Schema-based query parameter:  Pipe-delimited lists of 
comma-delimited lists of integers.</jc>
                <ja>@Query</ja>(
@@ -45,24 +59,13 @@
                        items=<ja>@Items</ja>(
                                items=<ja>@SubItems</ja>(
                                        collectionFormat=<js>"csv"</js>
-                                       type=<js>"integer"</js>
-                               )
+                                       type=<js>"integer"</js>,
+                                       minimum=1,
+                                       maximum=100
+                               ),
+                               maximumLength=10
                        )
                )
-               <jk>int</jk>[][] getQ3();
-
-               <ja>@Header</ja>(<js>"*"</js>)
-               Map&lt;String,Object&gt; getHeaders();
-</p>
-<p class='bpcode w800'>
-       <jc>// Same as above but annotation defined on interface.</jc>
-       <ja>@RestMethod</ja>(path=<js>"/mypath/{p1}/{p2}/*"</js>)
-       <jk>public void</jk> myMethod(MyRequestBean rb) {...}
-
-       <ja>@RequestBean</ja>
-       <jk>public interface</jk> MyRequestBean {...}
-</p>
-<p>
-       The return types of the getters must be the supported parameter types 
for the HTTP-part annotation used.
-       <br>Schema-based serialization and parsing is used just as if used as 
individual parameter types.
+               <jk>int</jk>[][] getPipeCdlInts();
+       }
 </p>
diff --git 
a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/09.ResponseBody.html
 
b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/09.ResponseBody.html
index 182ce0b..c54fb9f 100644
--- 
a/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/09.ResponseBody.html
+++ 
b/juneau-doc/src/main/resources/Topics/07.juneau-rest-server/09.HttpPartAnnotations/09.ResponseBody.html
@@ -16,4 +16,79 @@
 {todo} @ResponseBody
 
 <p>
+       The {@link org.apache.juneau.http.annotation.ResponseBody 
@ResponseBody} annotation is used to identify a placeholder parameter
+       for an HTTP response.
 </p>
+<ul class='doctree'>
+       <li class='ja'>{@link org.apache.juneau.http.annotation.ResponseBody 
ResponseBody}
+       <ul>
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#api() api()} - Free-form Swagger 
JSON.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#example() example()} - 
Serialized example.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#examples() examples()} - 
Serialized examples per media type.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#partSerializer() 
partSerializer()} - Override the part serializer.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#schema() schema()} - Swagger 
schema.
+               <li class='jf'>{@link 
org.apache.juneau.http.annotation.ResponseBody#usePartSerializer() 
usePartSerializer()} - Use the HTTP-Part serializer for serializing the body.
+       </ul>
+</ul>
+<p>
+       It can be used in the following locations:
+</p>
+<ul>
+       <li>Parameters on <ja>@RestMethod</ja>-annotated methods.
+       <li>Classes of parameters on <ja>@RestMethod</ja>-annotated methods.
+       <li><ja>@RestMethod</ja>-annotated methods.
+</ul>
+
+<h5 class='topic'>Parameters on @RestMethod-annotated methods</h5>
+<p>
+       When used on a method parameter, this annotation is used to identify a 
placeholder {@link org.apache.juneau.Value}
+       object that can be used to return the body of the response.
+</p>
+<p class='bpcode w800'>
+       <ja>@RestMethod</ja>(path=<js>"/pet/{petId}"</js>)
+       <jk>public void</jk> getPet(<ja>@Path</ja>(<js>"petId"</js>) 
<jk>int</jk> id, <ja>@ResponseBody</ja> Value&lt;Pet&gt; body) {
+               body.set(<jsm>getPet</jsm>(id));
+       }
+</p>
+<p>
+       The behavior is identical to the following:
+</p>
+<p class='bpcode w800'>
+       <ja>@RestMethod</ja>(path="/pet/{petId}")
+       <jk>public</jk> Pet getPet(<ja>@Path</ja>(<js>"petId"</js>) 
<jk>int</jk> id) {
+               <jk>return</jk> <jsm>getPet</jsm>(id);
+       }
+</p>
+<p>
+       You may ask why you would use ever use this.
+       The answer is that it's possible to have your method returns something 
else such as an HTTP response status
+       or header value.
+       This annotation allows you to set a response body when your method is 
returning one of these.
+       <br>Also, the annotation allows you to define swagger validation and 
documentation for your body.
+</p>
+
+<h5 class='topic'>Classes of parameters on @RestMethod-annotated methods</h5>
+<p>
+       The <ja>@ResponseBody</ja> annotation can also be applied to types.
+</p>
+<p class='bpcode w800'>
+       <ja>@RestMethod</ja>(path=<js>"/pet/{petId}"</js>)
+       <jk>public void</jk> getPet(<ja>@Path</ja>(<js>"petId"</js>) 
<jk>int</jk> id, Value&lt;Pet&gt; body) {
+               body.set(<jsm>getPet</jsm>(id));
+       }
+</p>
+<p class='bpcode w800'>
+       <ja>@ResponseBody</ja>(
+               schema=<ja>@Schema</ja>(
+                       description=<js>"Pet bean"</js>
+               )
+       )
+       <jk>public class</jk> Pet {...}
+</p>
+<p>
+       Note that this can often be cleaner-looking than usage on the Java 
method parameter.
+</p>
+
+<h5 class='topic'>@RestMethod-annotated methods</h5>
+
+
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 3170923..1bed25a 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,27 +379,36 @@ 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)) {
                                        for (Integer code : getCodes(a, 200)) {
                                                ObjectMap om = 
responses.getObjectMap(String.valueOf(code), true);
                                                merge(om, a);
+                                               usePS |= usePartSerializer(a);
+                                       }
+                               }
+                               if (usePS) {
+                                       for (String code : responses.keySet()) {
+                                               ObjectMap om = 
responses.getObjectMap(code);
                                                if (! om.containsKey("schema"))
                                                        
om.appendSkipEmpty("schema", getSchema(om.getObjectMap("schema", true), 
m.getGenericReturnType()));
                                        }
                                }
                        }
 
-                       if (hasAnnotation(ResponseBody.class, m)) {
-                               ObjectMap om = responses.getObjectMap("200", 
true);
-                               for (ResponseBody a : 
getAnnotationsParentFirst(ResponseBody.class, m)) {
-                                       merge(om, a);
-                                       if (! om.containsKey("schema"))
-                                               om.appendSkipEmpty("schema", 
getSchema(om.getObjectMap("schema", true), m.getGenericReturnType()));
-                               }
-                               addXExamples(sm, om, "ok", 
m.getGenericReturnType());
-                       }
-
                        // Finally, look for @ResponseHeader parameters defined 
on method.
                        for (RestMethodParam mp : 
context.getRestMethodParams(m)) {
 
@@ -414,24 +423,33 @@ final class SwaggerGenerator {
                                        }
 
                                } else if (in == RESPONSE) {
+                                       boolean usePS = false;
                                        for (Response a : 
getAnnotationsParentFirst(Response.class, mp.method, mp.index)) {
                                                for (Integer code : getCodes(a, 
200)) {
                                                        ObjectMap response = 
responses.getObjectMap(String.valueOf(code), true);
                                                        merge(response, a);
-                                                       if (! 
response.containsKey("schema")) {
-                                                               Type type = 
Value.getParameterType(mp.type);
-                                                               if (type != 
null)
-                                                                       
response.appendSkipEmpty("schema", getSchema(response.getObjectMap("schema", 
true), type));
+                                                       usePS |= 
usePartSerializer(a);
+                                               }
+                                       }
+                                       if (usePS) {
+                                               Type type = 
Value.getParameterType(mp.type);
+                                               if (type != null) {
+                                                       for (String code : 
responses.keySet()) {
+                                                               ObjectMap om = 
responses.getObjectMap(code);
+                                                               if (! 
om.containsKey("schema"))
+                                                                       
om.appendSkipEmpty("schema", getSchema(om.getObjectMap("schema", true), type));
                                                        }
                                                }
                                        }
 
                                } 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 (! response.containsKey("schema")) {
+                                       if (usePS && ! 
response.containsKey("schema")) {
                                                Type type = 
Value.getParameterType(mp.type);
                                                if (type != null)
                                                        
response.appendSkipEmpty("schema", getSchema(response.getObjectMap("schema", 
true), type));
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
index 618bc6c..139c47c 100644
--- 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
@@ -2196,7 +2196,7 @@ public class BasicRestInfoProviderTest {
                @RestMethod(name=GET,path="/path/{foo}/responses/100")
                public OE01x doFoo() {return null;}
        }
-       @Response(code=100)
+       @Response(code=100,usePartSerializer=true)
        public static class OE01x extends Foo {}
 
        @Test
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 692bd1e..c2e6a59 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
@@ -659,7 +659,7 @@ public class ResponseAnnotationTest {
                @RestMethod
                public SB01 sb01b() {return null;}
 
-               @Response
+               @Response(usePartSerializer=true)
                public static class SB02 {
                        public String f1;
                }
@@ -668,7 +668,7 @@ public class ResponseAnnotationTest {
                @RestMethod
                public SB02 sb02b() {return null;}
 
-               @Response
+               @Response(usePartSerializer=true)
                public static class SB03 extends LinkedList<String> {
                        private static final long serialVersionUID = 1L;
                }
@@ -677,7 +677,7 @@ public class ResponseAnnotationTest {
                @RestMethod
                public SB03 sb03b() {return null;}
 
-               @Response
+               @Response(usePartSerializer=true)
                public static class SB04 {}
                @RestMethod
                public void sb04a(Value<SB04> b) {}
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
new file mode 100644
index 0000000..0324663
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/ResponseBodyAnnotationTest.java
@@ -0,0 +1,452 @@
+// 
***************************************************************************************************************************
+// * 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.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);
+       }
+
+}

Reply via email to