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 b15e810  Tests
b15e810 is described below

commit b15e810fd1862cdbadf2aad38f238f54314c0f79
Author: JamesBognar <jamesbog...@apache.org>
AuthorDate: Wed May 9 18:49:19 2018 -0400

    Tests
---
 .../apache/juneau/utils/TransformCacheTest.java    | 170 ++++++++++++
 .../org/apache/juneau/internal/ClassUtils.java     |  98 ++-----
 .../java/org/apache/juneau/internal/Transform.java |  30 +++
 .../org/apache/juneau/internal/TransformCache.java | 186 +++++++++++++
 .../apache/juneau/rest/test/NoParserInputTest.java |  13 +-
 .../org/apache/juneau/rest/RestMethodParam.java    |  52 +++-
 .../org/apache/juneau/rest/RestParamDefaults.java  | 300 ++++++++++-----------
 .../org/apache/juneau/rest/annotation/Body.java    |   6 +-
 .../juneau/rest/BasicRestInfoProviderTest.java     |  17 +-
 .../apache/juneau/rest/annotation/BodyTest.java    | 157 +++++++++--
 10 files changed, 740 insertions(+), 289 deletions(-)

diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/TransformCacheTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/TransformCacheTest.java
new file mode 100644
index 0000000..49c16ed
--- /dev/null
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/TransformCacheTest.java
@@ -0,0 +1,170 @@
+// 
***************************************************************************************************************************
+// * 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.utils;
+
+import static org.apache.juneau.internal.TransformCache.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+public class TransformCacheTest {
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Constructors.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       public static class A {
+               private String f;
+               public A(String f) {
+                       this.f = f;
+               }
+               public A(int f) {
+                       this.f = String.valueOf(f);
+               }
+               public A(Integer f) {
+                       this.f = String.valueOf(f);
+               }
+       }
+       @Test
+       public void stringConstructor() {
+               assertEquals("foo", get(String.class, 
A.class).transform("foo").f);
+       }
+       @Test
+       public void intConstructor() {
+               assertEquals("1", get(int.class, A.class).transform(1).f);
+       }
+       @Test
+       public void integerConstructor() {
+               assertEquals("2", get(Integer.class, A.class).transform(2).f);
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // fromString methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       public static class D1 {
+               private String f;
+               public static D1 create(String f) {
+                       D1 d = new D1(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_create() {
+               assertEquals("foo", get(String.class, 
D1.class).transform("foo").f);
+       }
+       
+       public static class D2 {
+               private String f;
+               public static D2 fromString(String f) {
+                       D2 d = new D2(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_fromString() {
+               assertEquals("foo", get(String.class, 
D2.class).transform("foo").f);
+       }
+
+       public static class D3 {
+               private String f;
+               public static D3 fromValue(String f) { 
+                       D3 d = new D3(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_fromValue() {
+               assertEquals("foo", get(String.class, 
D3.class).transform("foo").f);
+       }
+
+       public static class D4 {
+               private String f;
+               public static D4 valueOf(String f) {
+                       D4 d = new D4(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_valueOf() {
+               assertEquals("foo", get(String.class, 
D4.class).transform("foo").f);
+       }
+
+       public static class D5 {
+               private String f;
+               public static D5 parse(String f) {
+                       D5 d = new D5(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_parse() {
+               assertEquals("foo", get(String.class, 
D5.class).transform("foo").f);
+       }
+
+       public static class D6 {
+               private String f;
+               public static D6 parseString(String f) {
+                       D6 d = new D6(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_parseString() {
+               assertEquals("foo", get(String.class, 
D6.class).transform("foo").f);
+       }
+
+       public static class D7 {
+               private String f;
+               public static D7 forName(String f) {
+                       D7 d = new D7(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_forName() {
+               assertEquals("foo", get(String.class, 
D7.class).transform("foo").f);
+       }
+
+       public static class D8 {
+               private String f;
+               public static D8 forString(String f) {
+                       D8 d = new D8(); d.f = f; return d;
+               }
+       }
+       @Test
+       public void fromString_forString() {
+               assertEquals("foo", get(String.class, 
D8.class).transform("foo").f);
+       }
+       
+       
//-----------------------------------------------------------------------------------------------------------------
+       // fromX methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+       
+       public static class X {}
+       
+       public static class E1 {
+               private String f;
+               public static E1 create(X x) {
+                       E1 e = new E1(); e.f = "ok"; return e;
+               }
+       }
+       @Test
+       public void fromX_create() {
+               assertEquals("ok", get(X.class, E1.class).transform(new X()).f);
+       }
+       
+       public static class E2 {
+               private String f;
+               public static E2 fromX(X x) {
+                       E2 e = new E2(); e.f = "ok"; return e;
+               }
+       }
+       @Test
+       public void fromX_fromX() {
+               assertEquals("ok", get(X.class, E2.class).transform(new X()).f);
+       }
+}
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 7ebbe94..da6c49b 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
@@ -28,31 +28,6 @@ import org.apache.juneau.utils.*;
 public final class ClassUtils {
 
        private static final Map<Class<?>,ConstructorCacheEntry> 
CONSTRUCTOR_CACHE = new ConcurrentHashMap<>();
-       private static final Map<Class<?>,Stringify<?>> STRINGIFY_CACHE = new 
ConcurrentHashMap<>();
-       
-       // Special cases.
-       static {
-               
-               // TimeZone doesn't follow any standard conventions.
-               STRINGIFY_CACHE.put(TimeZone.class, new 
Stringify<TimeZone>(TimeZone.class){
-                       @Override 
-                       public TimeZone fromString(String s) {
-                               return TimeZone.getTimeZone(s);
-                       }
-                       @Override 
-                       public String toString(TimeZone tz) {
-                               return tz.getID();
-                       }
-               });
-               
-               // Locale(String) doesn't work on strings like "ja_JP".
-               STRINGIFY_CACHE.put(Locale.class, new 
Stringify<Locale>(Locale.class){
-                       @Override 
-                       public Locale fromString(String s) {
-                               return Locale.forLanguageTag(s.replace('_', 
'-'));
-                       }
-               });
-       }
        
        /**
         * Given the specified list of objects, return readable names for the 
class types of the objects.
@@ -1897,6 +1872,21 @@ public final class ClassUtils {
        }
        
        /**
+        * Find the public static creator method on the specified class.
+        * 
+        * @param oc The created type.
+        * @param ic The argument type.
+        * @param name The method name.
+        * @return The static method, or <jk>null</jk> if it couldn't be found.
+        */
+       public static Method findPublicStaticCreateMethod(Class<?> oc, Class<?> 
ic, String name) {
+               for (Method m : oc.getMethods()) 
+                       if (isAll(m, STATIC, PUBLIC, NOT_DEPRECATED) && 
hasName(m, name) && hasReturnType(m, oc) && hasArgs(m, ic)) 
+                               return m;
+               return null;
+       }
+
+       /**
         * Constructs a new instance of the specified class from the specified 
string.
         * 
         * <p>
@@ -1912,9 +1902,9 @@ public final class ClassUtils {
         * @param s The string to create the instance from.
         * @return A new object instance, or <jk>null</jk> if a method for 
converting the string to an object could not be found.
         */
-       @SuppressWarnings({ "unchecked" })
        public static <T> T fromString(Class<T> c, String s) {
-               return (T)getStringify(c).fromString(s);
+               Transform<String,T> t = TransformCache.get(String.class, c);
+               return t == null ? null : t.transform(s);
        }
        
        /**
@@ -1932,7 +1922,8 @@ public final class ClassUtils {
        public static String toString(Object o) {
                if (o == null)
                        return null;
-               return getStringify(o.getClass()).toString(o);
+               Transform<Object,String> t = 
(Transform<Object,String>)TransformCache.get(o.getClass(), String.class);
+               return t == null ? o.toString() : t.transform(o);
        }
        
        /**
@@ -1992,57 +1983,6 @@ public final class ClassUtils {
                }
        }
        
-       @SuppressWarnings({ "unchecked", "rawtypes" })
-       private static Stringify getStringify(Class c) {
-               Stringify fs = STRINGIFY_CACHE.get(c);
-               if (fs == null) {
-                       for (Iterator<Class<?>> i = getParentClasses(c, false, 
true); i.hasNext(); ) {
-                               Class c2 = i.next();
-                               fs = STRINGIFY_CACHE.get(c2);
-                               if (fs != null) {
-                                       STRINGIFY_CACHE.put(c, fs);
-                                       break;
-                               }
-                       }
-                       if (fs == null) {
-                               fs = new Stringify(c);
-                               STRINGIFY_CACHE.put(c, fs);
-                       }
-               }
-               return fs;
-       }
-       
-       @SuppressWarnings({"unchecked","rawtypes"})
-       private static class Stringify<T> {
-               final Constructor<?> constructor;
-               final Method fromStringMethod;
-               final Class<? extends Enum> enumClass;
-               
-               Stringify(Class<?> c) {
-                       enumClass = c.isEnum() ? (Class<? extends Enum>)c : 
null;
-                       fromStringMethod = enumClass != null ? null : 
findPublicFromStringMethod(c);
-                       constructor = enumClass != null || fromStringMethod != 
null ? null : findPublicConstructor(c, String.class);
-               }
-               
-               public T fromString(String s) {
-                       try {
-                               if (fromStringMethod != null)
-                                       return (T)fromStringMethod.invoke(null, 
s);
-                               if (constructor != null)
-                                       return (T)constructor.newInstance(s);
-                               if (enumClass != null)
-                                       return (T)Enum.valueOf(enumClass, s);
-                               return null;
-                       } catch (Exception e) {
-                               throw new RuntimeException(e);
-                       }
-               }
-
-               public String toString(T t) {
-                       return t.toString(); 
-               }
-       }
-       
        /**
         * Returns the simple name of a class.
         * 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Transform.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Transform.java
new file mode 100644
index 0000000..9d673aa
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/Transform.java
@@ -0,0 +1,30 @@
+// 
***************************************************************************************************************************
+// * 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.internal;
+
+/**
+ * An interface for creating objects from other objects such as a 
<code>String</code> or <code>Reader</code>.
+ * 
+ * @param <I> Input type.
+ * @param <O> Output type.
+ */
+public interface Transform<I,O> {
+       
+       /**
+        * Method for instantiating an object from another object.
+        * 
+        * @param in The input object.
+        * @return The output object.
+        */
+       public O transform(I in);
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/TransformCache.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/TransformCache.java
new file mode 100644
index 0000000..0869e05
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/TransformCache.java
@@ -0,0 +1,186 @@
+// 
***************************************************************************************************************************
+// * 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.internal;
+
+import java.util.concurrent.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * Cache of object that convert POJOs to and from common types such as 
strings, readers, and input streams.
+ */
+public class TransformCache {
+       private static final 
ConcurrentHashMap<Class<?>,Map<Class<?>,Transform<?,?>>> CACHE = new 
ConcurrentHashMap<>();
+       
+       /**
+        * Represents a non-existent transform.
+        */
+       public static final Transform<Object,Object> NULL = new 
Transform<Object,Object>() {
+               @Override
+               public Object transform(Object in) {
+                       return null;
+               }
+       };
+       
+       // Special cases.
+       static {
+               
+               // TimeZone doesn't follow any standard conventions.
+               add(String.class, TimeZone.class,
+                       new Transform<String,TimeZone>() {
+                               @Override public TimeZone transform(String in) {
+                                       return TimeZone.getTimeZone(in);
+                               }
+                       }
+               );
+               add(TimeZone.class, String.class, 
+                       new Transform<TimeZone,String>() {
+                               @Override public String transform(TimeZone in) {
+                                       return in.getID();
+                               }
+                       }
+               );
+               
+               // Locale(String) doesn't work on strings like "ja_JP".
+               add(String.class, Locale.class,
+                       new Transform<String,Locale>() {
+                               @Override
+                               public Locale transform(String in) {
+                                       return 
Locale.forLanguageTag(in.replace('_', '-'));
+                               }
+                       }
+               );
+       }
+       
+       /**
+        * Adds a transform for the specified input/output types.
+        * 
+        * @param ic The input type.
+        * @param oc The output type.
+        * @param t The transform for converting the input to the output.
+        */
+       public static synchronized void add(Class<?> ic, Class<?> oc, 
Transform<?,?> t) {
+               Map<Class<?>,Transform<?,?>> m = CACHE.get(oc);
+               if (m == null) {
+                       m = new ConcurrentHashMap<>();
+                       CACHE.put(oc, m);
+               }
+               m.put(ic, t);
+       }
+       
+       /**
+        * Returns the transform for converting the specified input type to the 
specified output type.
+        * 
+        * @param ic The input type.
+        * @param oc The output type.
+        * @return The transform for performing the conversion, or 
<jk>null</jk> if the conversion cannot be made.
+        */
+       @SuppressWarnings({ "unchecked", "rawtypes" })
+       public static <I,O> Transform<I,O> get(final Class<I> ic, final 
Class<O> oc) {
+               
+               if (ic == null || oc == null)
+                       return null;
+               
+               Map<Class<?>,Transform<?,?>> m = CACHE.get(oc);
+               if (m == null) {
+                       m = new ConcurrentHashMap<>();
+                       CACHE.putIfAbsent(oc, m);
+                       m = CACHE.get(oc);
+               }
+               
+               Transform t = m.get(ic);
+               if (t != null)
+                       return t == NULL ? null : t;
+
+               for (Iterator<Class<?>> i = ClassUtils.getParentClasses(ic, 
false, true); i.hasNext(); ) {
+                       Class pic = i.next();
+                       t = m.get(pic);
+                       if (t != null) {
+                               m.put(pic, t);
+                               return t == NULL ? null : t;
+                       }
+               }
+               
+               if (ic == oc) {
+                       t = new Transform<I,O>() {
+                               @Override public O transform(I in) {
+                                       return (O)in;
+                               }
+                       };
+               } else if (ic == String.class) {
+                       if (oc.isEnum()) {
+                               t = new Transform<String,O>() {
+                                       @Override
+                                       public O transform(String in) {
+                                               return (O)Enum.valueOf((Class<? 
extends Enum>)oc, in);
+                                       }
+                               };
+                       } else {
+                               final Method fromStringMethod = 
ClassUtils.findPublicFromStringMethod(oc);
+                               if (fromStringMethod != null) {
+                                       t = new Transform<String,O>() {
+                                               @Override
+                                               public O transform(String in) {
+                                                       try {
+                                                               return 
(O)fromStringMethod.invoke(null, in);
+                                                       } catch (Exception e) {
+                                                               throw new 
RuntimeException(e);
+                                                       }
+                                               }
+                                       };
+                               }
+                       }
+               }
+               
+               if (t == null) {
+                       Method createMethod = 
ClassUtils.findPublicStaticCreateMethod(oc, ic, "create");
+                       if (createMethod == null)
+                               createMethod = 
ClassUtils.findPublicStaticCreateMethod(oc, ic, "from" + ic.getSimpleName());
+                       if (createMethod != null) {
+                               final Method cm = createMethod;
+                               t = new Transform<I,O>() {
+                                       @Override
+                                       public O transform(I in) {
+                                               try {
+                                                       return 
(O)cm.invoke(null, in);
+                                               } catch (Exception e) {
+                                                       throw new 
RuntimeException(e);
+                                               }
+                                       }
+                               };
+                       } else {
+                               final Constructor<?> c = 
ClassUtils.findPublicConstructor(oc, ic);
+                               if (c != null && ! ClassUtils.isDeprecated(c)) {
+                                       t = new Transform<I,O>() {
+                                               @Override
+                                               public O transform(I in) {
+                                                       try {
+                                                               return 
(O)c.newInstance(in);
+                                                       } catch (Exception e) {
+                                                               throw new 
RuntimeException(e);
+                                                       }
+                                               }
+                                       };
+                               }
+                               
+                       }
+               }
+               
+               if (t == null)
+                       t = NULL;
+               
+               m.put(ic, t);
+               
+               return t == NULL ? null : t;
+       }
+}
diff --git 
a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/NoParserInputTest.java
 
b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/NoParserInputTest.java
index 0c9b2a3..fa0ce0a 100644
--- 
a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/NoParserInputTest.java
+++ 
b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/NoParserInputTest.java
@@ -12,8 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.rest.test;
 
-import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.rest.test.TestUtils.*;
 import static org.junit.Assert.*;
 
 import org.apache.juneau.rest.client.*;
@@ -45,17 +43,10 @@ public class NoParserInputTest extends RestTestcase {
 
        
//====================================================================================================
        // @Body annotated PushbackReader.
-       // This should always fail since the servlet reader is not a pushback 
reader.
        
//====================================================================================================
        @Test
        public void testPushbackReader() throws Exception {
-               try {
-                       plainTextClient.doPut(URL + 
"/testPushbackReader?noTrace=true", "foo").getResponseAsString();
-                       fail("Exception expected");
-               } catch (RestCallException e) {
-                       checkErrorResponse(debug, e, SC_BAD_REQUEST,
-                               "Invalid argument type passed to the following 
method:",
-                               "'public java.lang.String 
org.apache.juneau.rest.test.NoParserInputResource.testPushbackReader(java.io.PushbackReader)
 throws java.lang.Exception'");
-               }
+               String r = plainTextClient.doPut(URL + "/testPushbackReader", 
"foo").getResponseAsString();
+               assertEquals("foo", r);
        }
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
index ebaf289..31692dc 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
@@ -115,38 +115,67 @@ import org.apache.juneau.utils.*;
 public abstract class RestMethodParam {
 
        final RestParamType paramType;
+       final Method method;
        final String name;
        final Type type;
+       final Class<?> c;
        final ObjectMap metaData;
 
        /**
         * Constructor.
         * 
         * @param paramType The Swagger parameter type.
+        * @param method The method on which the parameter resides.
+        * @param name
+        *      The parameter name.
+        *      Can be <jk>null</jk> if parameter doesn't have a name (e.g. the 
request body).
+        * @param type The object type to convert the parameter to.
+        * @param metaData Swagger metadata.
+        */
+       protected RestMethodParam(RestParamType paramType, Method method, 
String name, Type type, ObjectMap metaData) {
+               this.paramType = paramType;
+               this.method = method;
+               this.name = name;
+               this.type = type;
+               this.c = type instanceof Class ? (Class<?>)type : type 
instanceof ParameterizedType ? (Class<?>)((ParameterizedType)type).getRawType() 
: null;
+               this.metaData = metaData;
+       }
+
+       /**
+        * Constructor.
+        * 
+        * @param paramType The Swagger parameter type.
+        * @param type The object type to convert the parameter to.
+        */
+       protected RestMethodParam(RestParamType paramType, Type type) {
+               this(paramType, null, null, type, ObjectMap.EMPTY_MAP);
+       }
+
+       /**
+        * Constructor.
+        * 
+        * @param paramType The Swagger parameter type.
         * @param name
         *      The parameter name.
         *      Can be <jk>null</jk> if parameter doesn't have a name (e.g. the 
request body).
         * @param type The object type to convert the parameter to.
         */
        protected RestMethodParam(RestParamType paramType, String name, Type 
type) {
-               this(paramType, name, type, ObjectMap.EMPTY_MAP);
+               this(paramType, null, name, type, ObjectMap.EMPTY_MAP);
        }
 
        /**
         * Constructor.
         * 
         * @param paramType The Swagger parameter type.
+        * @param method The method on which the parameter resides.
         * @param name
         *      The parameter name.
         *      Can be <jk>null</jk> if parameter doesn't have a name (e.g. the 
request body).
         * @param type The object type to convert the parameter to.
-        * @param metaData Swagger metadata.
         */
-       protected RestMethodParam(RestParamType paramType, String name, Type 
type, ObjectMap metaData) {
-               this.paramType = paramType;
-               this.name = name;
-               this.type = type;
-               this.metaData = metaData;
+       protected RestMethodParam(RestParamType paramType, Method method, 
String name, Type type) {
+               this(paramType, method, name, type, ObjectMap.EMPTY_MAP);
        }
 
        /**
@@ -207,6 +236,15 @@ public abstract class RestMethodParam {
        }
        
        /**
+        * Returns the parameter class type.
+        * 
+        * @return the parameter class type.
+        */
+       public Class<?> getTypeClass() {
+               return c;
+       }
+       
+       /**
         * Performs validation on the parameter.
         * 
         * @throws InternalServerError
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index aef5e5b..a0a33c0 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -145,7 +145,7 @@ class RestParamDefaults {
        static final class HttpServletRequestObject extends RestMethodParam {
 
                protected HttpServletRequestObject() {
-                       super(OTHER, null, HttpServletRequest.class);
+                       super(OTHER, HttpServletRequest.class);
                }
 
                @Override /* RestMethodParam */
@@ -157,7 +157,7 @@ class RestParamDefaults {
        static final class HttpServletResponseObject extends RestMethodParam {
 
                protected HttpServletResponseObject() {
-                       super(OTHER, null, HttpServletResponse.class);
+                       super(OTHER, HttpServletResponse.class);
                }
 
                @Override /* RestMethodParam */
@@ -169,7 +169,7 @@ class RestParamDefaults {
        static final class RestRequestObject extends RestMethodParam {
 
                protected RestRequestObject() {
-                       super(OTHER, null, RestRequest.class);
+                       super(OTHER, RestRequest.class);
                }
 
                @Override /* RestMethodParam */
@@ -181,7 +181,7 @@ class RestParamDefaults {
        static final class RestResponseObject extends RestMethodParam {
 
                protected RestResponseObject() {
-                       super(OTHER, null, RestResponse.class);
+                       super(OTHER, RestResponse.class);
                }
 
                @Override /* RestMethodParam */
@@ -547,11 +547,15 @@ class RestParamDefaults {
        
//-------------------------------------------------------------------------------------------------------------------
 
        static final class PathObject extends RestMethodParam {
-               private final Method method;
 
                protected PathObject(Method method, Path a, Type type, 
PropertyStore ps, RestMethodParam existing) {
-                       super(PATH, firstNonEmpty(a.name(), a.value(), existing 
== null ? null : existing.name), type, getMetaData(a, castOrNull(existing, 
PathObject.class)));
-                       this.method = method;
+                       super(PATH, method, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, PathObject.class)));
+               }
+
+               @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@Path used 
without name or value on method ''{0}''.", method);
                }
 
                @Override /* RestMethodParam */
@@ -581,22 +585,25 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
-               
-               @Override /* RestMethodParqm */
-               public void validate() throws InternalServerError {
-                       if (isEmpty(name))
-                               throw new InternalServerError("@Path used 
without name or value on method ''{0}''.", method);
-               }
        }
 
        static final class BodyObject extends RestMethodParam {
+               
+               private final Transform<Reader,?> readerTransform;
+               private final Transform<InputStream,?> inputStreamTransform;
 
                protected BodyObject(Method method, Body a, Type type, 
RestMethodParam existing) {
-                       super(BODY, null, type, getMetaData(a, 
castOrNull(existing, BodyObject.class)));
+                       super(BODY, method, null, type, getMetaData(a, 
castOrNull(existing, BodyObject.class)));
+                       readerTransform = TransformCache.get(Reader.class, 
getTypeClass());
+                       inputStreamTransform = 
TransformCache.get(InputStream.class, getTypeClass());
                }
 
                @Override /* RestMethodParam */
                public Object resolve(RestRequest req, RestResponse res) throws 
Exception {
+                       if (readerTransform != null)
+                               return 
readerTransform.transform(req.getReader());  // Also passes Reader through.
+                       if (inputStreamTransform != null)
+                               return 
inputStreamTransform.transform(req.getInputStream());  // Also passes 
InputStream through.
                        return req.getBody().asType(type);
                }
                
@@ -633,16 +640,20 @@ class RestParamDefaults {
        }
 
        static final class HeaderObject extends RestMethodParam {
-               private final Method method;
                private final HttpPartParser partParser;
 
                protected HeaderObject(Method method, Header a, Type type, 
PropertyStore ps, RestMethodParam existing) {
-                       super(HEADER, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, HeaderObject.class)));
-                       this.method = method;
+                       super(HEADER, method, firstNonEmpty(a.name(), 
a.value(), existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, HeaderObject.class)));
                        this.partParser = a.parser() == 
HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, 
a.parser(), true, ps);
                }
 
                @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@Header used 
without name or value on method ''{0}''.", method);
+               }
+
+               @Override /* RestMethodParam */
                public Object resolve(RestRequest req, RestResponse res) throws 
Exception {
                        return req.getHeaders().get(partParser, name, type);
                }
@@ -676,51 +687,42 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
-               
-               @Override /* RestMethodParqm */
-               public void validate() throws InternalServerError {
-                       if (isEmpty(name))
-                               throw new InternalServerError("@Header used 
without name or value on method ''{0}''.", method);
-               }
        }
 
        static final class ResponseHeaderObject extends RestMethodParam {
-               private final Method method;
                final HttpPartSerializer partSerializer;
 
                protected ResponseHeaderObject(Method method, ResponseHeader a, 
Type type, PropertyStore ps, RestMethodParam existing) {
-                       super(RESPONSE_HEADER, firstNonEmpty(a.name(), 
a.value(), existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, ResponseHeaderObject.class)));
-                       this.method = method;
+                       super(RESPONSE_HEADER, method, firstNonEmpty(a.name(), 
a.value(), existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, ResponseHeaderObject.class)));
                        this.partSerializer = a.serializer() == 
HttpPartSerializer.Null.class ? null : 
ClassUtils.newInstance(HttpPartSerializer.class, a.serializer(), true, ps);
                }
 
+               @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@ResponseHeader 
used without name or value on method ''{0}''.", method);
+                       if (getTypeClass() == null) 
+                               throw new InternalServerError("Invalid type {0} 
specified with @ResponseHeader annotation.  It must be a subclass of Value.", 
type);
+                       if (ClassUtils.findNoArgConstructor(getTypeClass(), 
Visibility.PUBLIC) == null)
+                               throw new InternalServerError("Invalid type {0} 
specified with @ResponseHeader annotation.  It must have a public no-arg 
constructor.", type);
+               }
+
                @SuppressWarnings({ "unchecked", "rawtypes" })
                @Override /* RestMethodParam */
                public Object resolve(RestRequest req, final RestResponse res) 
throws Exception {
-                       if (type instanceof Class) {
-                               Class<?> c = (Class<?>)type;
-                               if (ClassUtils.isParentClass(Value.class, c)) {
-                                       try {
-                                               Value<Object> v = 
(Value<Object>)c.newInstance();
-                                               v.listener(new ValueListener() {
-                                                       @Override
-                                                       public void 
onSet(Object newValue) {
-                                                               
res.setHeader(name, partSerializer.serialize(HttpPartType.HEADER, newValue));
-                                                       }
-                                               });
-                                               String def = 
getMetaData().getString("default");
-                                               if (def != null) {
-                                                       Class<?> pc = 
ClassUtils.resolveParameterType(Value.class, 0, c);
-                                                       
v.set(JsonParser.DEFAULT.parse(def, req.getBeanSession().getClassMeta(pc)));
-                                               }
-                                                       
-                                               return v;
-                                       } catch (Exception e) {
-                                               throw new RestException(500, 
"Invalid type {0} specified with @ResponseHeader annotation.  It must have a 
public no-arg constructor.", type);
-                                       }
+                       Value<Object> v = 
(Value<Object>)getTypeClass().newInstance();
+                       v.listener(new ValueListener() {
+                               @Override
+                               public void onSet(Object newValue) {
+                                       res.setHeader(name, 
partSerializer.serialize(HttpPartType.HEADER, newValue));
                                }
+                       });
+                       String def = getMetaData().getString("default");
+                       if (def != null) {
+                               Class<?> pc = 
ClassUtils.resolveParameterType(Value.class, 0, c);
+                               v.set(JsonParser.DEFAULT.parse(def, 
req.getBeanSession().getClassMeta(pc)));
                        }
-                       throw new RestException(500, "Invalid type {0} 
specified with @ResponseHeader annotation.  It must be a subclass of Value.", 
type);
+                       return v;
                }
                
                public HttpPartSerializer getPartSerializer() {
@@ -763,47 +765,38 @@ class RestParamDefaults {
                        }
                        return om;
                }
-               
-               @Override /* RestMethodParqm */
-               public void validate() throws InternalServerError {
-                       if (isEmpty(name))
-                               throw new InternalServerError("@ResponseHeader 
used without name or value on method ''{0}''.", method);
-               }
        }
 
        static final class ResponseObject extends RestMethodParam {
 
                protected ResponseObject(Method method, Response a, Type type, 
PropertyStore ps, RestMethodParam existing) {
-                       super(RESPONSE, "body", type, getMetaData(a, 
castOrNull(existing, ResponseObject.class)));
+                       super(RESPONSE, method, "body", type, getMetaData(a, 
castOrNull(existing, ResponseObject.class)));
+               }
+
+               @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (getTypeClass() == null) 
+                               throw new InternalServerError("Invalid type {0} 
specified with @Response annotation.  It must be a subclass of Value.", type);
+                       if (ClassUtils.findNoArgConstructor(getTypeClass(), 
Visibility.PUBLIC) == null)
+                               throw new InternalServerError("Invalid type {0} 
specified with @Response annotation.  It must have a public no-arg 
constructor.", type);
                }
 
                @SuppressWarnings({ "unchecked", "rawtypes" })
                @Override /* RestMethodParam */
                public Object resolve(RestRequest req, final RestResponse res) 
throws Exception {
-                       if (type instanceof Class) {
-                               Class<?> c = (Class<?>)type;
-                               if (ClassUtils.isParentClass(Value.class, c)) {
-                                       try {
-                                               Value<Object> v = 
(Value<Object>)c.newInstance();
-                                               v.listener(new ValueListener() {
-                                                       @Override
-                                                       public void 
onSet(Object newValue) {
-                                                               
res.setOutput(newValue);
-                                                       }
-                                               });
-                                               String def = 
getMetaData().getString("default");
-                                               if (def != null) {
-                                                       Class<?> pc = 
ClassUtils.resolveParameterType(Value.class, 0, c);
-                                                       
v.set(JsonParser.DEFAULT.parse(def, req.getBeanSession().getClassMeta(pc)));
-                                               }
-                                                       
-                                               return v;
-                                       } catch (Exception e) {
-                                               throw new RestException(500, 
"Invalid type {0} specified with @ResponseHeader annotation.  It must have a 
public no-arg constructor.", type);
-                                       }
+                       Value<Object> v = (Value<Object>)c.newInstance();
+                       v.listener(new ValueListener() {
+                               @Override
+                               public void onSet(Object newValue) {
+                                       res.setOutput(newValue);
                                }
+                       });
+                       String def = getMetaData().getString("default");
+                       if (def != null) {
+                               Class<?> pc = 
ClassUtils.resolveParameterType(Value.class, 0, c);
+                               v.set(JsonParser.DEFAULT.parse(def, 
req.getBeanSession().getClassMeta(pc)));
                        }
-                       throw new RestException(500, "Invalid type {0} 
specified with @ResponseHeader annotation.  It must be a subclass of Value.", 
type);
+                       return v;
                }
                
                private static ObjectMap getMetaData(Response a, ResponseObject 
existing) {
@@ -826,36 +819,33 @@ class RestParamDefaults {
        static final class ResponseStatusObject extends RestMethodParam {
 
                protected ResponseStatusObject(Method method, Status a, Type 
type, PropertyStore ps, RestMethodParam existing) {
-                       super(RESPONSE_STATUS, "", type, getMetaData(a, 
castOrNull(existing, ResponseStatusObject.class)));
+                       super(RESPONSE_STATUS, method, "", type, getMetaData(a, 
castOrNull(existing, ResponseStatusObject.class)));
+               }
+
+               @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (getTypeClass() == null) 
+                               throw new InternalServerError("Invalid type {0} 
specified with @ResponseStatus annotation.  It must be a subclass of Value.", 
type);
+                       if (ClassUtils.findNoArgConstructor(getTypeClass(), 
Visibility.PUBLIC) == null)
+                               throw new InternalServerError("Invalid type {0} 
specified with @ResponseStatus annotation.  It must have a public no-arg 
constructor.", type);
                }
 
                @SuppressWarnings({ "unchecked", "rawtypes" })
                @Override /* RestMethodParam */
                public Object resolve(RestRequest req, final RestResponse res) 
throws Exception {
-                       if (type instanceof Class) {
-                               Class<?> c = (Class<?>)type;
-                               if (ClassUtils.isParentClass(Value.class, c)) {
-                                       try {
-                                               Value<Object> v = 
(Value<Object>)c.newInstance();
-                                               v.listener(new ValueListener() {
-                                                       @Override
-                                                       public void 
onSet(Object newValue) {
-                                                               
res.setStatus(Integer.parseInt(newValue.toString()));
-                                                       }
-                                               });
-                                               String def = 
getMetaData().getString("default");
-                                               if (def != null) {
-                                                       Class<?> pc = 
ClassUtils.resolveParameterType(Value.class, 0, c);
-                                                       
v.set(JsonParser.DEFAULT.parse(def, req.getBeanSession().getClassMeta(pc)));
-                                               }
-                                                       
-                                               return v;
-                                       } catch (Exception e) {
-                                               throw new RestException(500, 
"Invalid type {0} specified with @Status annotation.  It must have a public 
no-arg constructor.", type);
-                                       }
+                       Value<Object> v = (Value<Object>)c.newInstance();
+                       v.listener(new ValueListener() {
+                               @Override
+                               public void onSet(Object newValue) {
+                                       
res.setStatus(Integer.parseInt(newValue.toString()));
                                }
+                       });
+                       String def = getMetaData().getString("default");
+                       if (def != null) {
+                               Class<?> pc = 
ClassUtils.resolveParameterType(Value.class, 0, c);
+                               v.set(JsonParser.DEFAULT.parse(def, 
req.getBeanSession().getClassMeta(pc)));
                        }
-                       throw new RestException(500, "Invalid type {0} 
specified with @Status annotation.  It must be a subclass of Value.", type);
+                       return v;
                }
                
                private static ObjectMap getMetaData(Status a, 
ResponseStatusObject existing) {
@@ -886,18 +876,22 @@ class RestParamDefaults {
        }
 
        static final class FormDataObject extends RestMethodParam {
-               private final Method method;
                private final boolean multiPart;
                private final HttpPartParser partParser;
-               private final Type type;
 
                protected FormDataObject(Method method, FormData a, Type type, 
PropertyStore ps, RestMethodParam existing) {
-                       super(FORM_DATA, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, FormDataObject.class)));
-                       this.method = method;
-                       this.type = type;
+                       super(FORM_DATA, method, firstNonEmpty(a.name(), 
a.value(), existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, FormDataObject.class)));
                        this.multiPart = a.multipart();
                        this.partParser = a.parser() == 
HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, 
a.parser(), true, ps);
                }
+               
+               @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@FormData used 
without name or value on method ''{0}''.", method);
+                       if (multiPart && ! isCollection(type))
+                               throw new InternalServerError("Use of multipart 
flag on @FormData parameter that's not an array or Collection on method 
''{0}''", method);
+               }
 
                @Override /* RestMethodParam */
                public Object resolve(RestRequest req, RestResponse res) throws 
Exception {
@@ -935,31 +929,27 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
-               
-               @Override /* RestMethodParqm */
-               public void validate() throws InternalServerError {
-                       if (isEmpty(name))
-                               throw new InternalServerError("@FormData used 
without name or value on method ''{0}''.", method);
-                       if (multiPart && ! isCollection(type))
-                               throw new InternalServerError("Use of multipart 
flag on @FormData parameter that's not an array or Collection on method 
''{0}''", method);
-               }
        }
 
        static final class QueryObject extends RestMethodParam {
                private final boolean multiPart;
-               private final Type type;
-               private final Method method;
                private final HttpPartParser partParser;
 
                protected QueryObject(Method method, Query a, Type type, 
PropertyStore ps, RestMethodParam existing) {
-                       super(QUERY, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, QueryObject.class)));
-                       this.type = type;
-                       this.method = method;
+                       super(QUERY, method, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, QueryObject.class)));
                        this.multiPart = a.multipart();
                        this.partParser = a.parser() == 
HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, 
a.parser(), true, ps);
                }
 
                @Override /* RestMethodParam */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@Query used 
without name or value on method ''{0}''.", method);
+                       if (multiPart && ! isCollection(type))
+                               throw new InternalServerError("Use of multipart 
flag on @Query parameter that's not an array or Collection on method ''{0}''", 
method);
+               }
+
+               @Override /* RestMethodParam */
                public Object resolve(RestRequest req, RestResponse res) throws 
Exception {
                        if (multiPart)
                                return req.getQuery().getAll(partParser, name, 
type);
@@ -995,20 +985,12 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
-               
-               @Override /* RestMethodParqm */
-               public void validate() throws InternalServerError {
-                       if (isEmpty(name))
-                               throw new InternalServerError("@Query used 
without name or value on method ''{0}''.", method);
-                       if (multiPart && ! isCollection(type))
-                               throw new InternalServerError("Use of multipart 
flag on @Query parameter that's not an array or Collection on method ''{0}''", 
method);
-               }
        }
 
        static final class HasFormDataObject extends RestMethodParam {
 
                protected HasFormDataObject(Method method, HasFormData a, Type 
type) throws ServletException {
-                       super(FORM_DATA, firstNonEmpty(a.name(), a.value()), 
type);
+                       super(FORM_DATA, method, firstNonEmpty(a.name(), 
a.value()), type);
                        if (type != Boolean.class && type != boolean.class)
                                throw new RestServletException("Use of @HasForm 
annotation on parameter that is not a boolean on method ''{0}''", method);
        }
@@ -1023,7 +1005,7 @@ class RestParamDefaults {
        static final class HasQueryObject extends RestMethodParam {
 
                protected HasQueryObject(Method method, HasQuery a, Type type) 
throws ServletException {
-                       super(QUERY, firstNonEmpty(a.name(), a.value()), type);
+                       super(QUERY, method, firstNonEmpty(a.name(), 
a.value()), type);
                        if (type != Boolean.class && type != boolean.class)
                                throw new RestServletException("Use of 
@HasQuery annotation on parameter that is not a boolean on method ''{0}''", 
method);
                }
@@ -1036,23 +1018,21 @@ class RestParamDefaults {
        }
 
        static final class PathRemainderObject extends RestMethodParam {
-
-               protected PathRemainderObject(Method method, Type type) throws 
ServletException {
-                       super(OTHER, null, null);
-                       if (type != String.class)
-                               throw new RestServletException("Use of 
@PathRemainder annotation on parameter that is not a String on method ''{0}''", 
method);
+               
+               protected PathRemainderObject(Method method, Type type) {
+                       super(OTHER, method, null, type);
                }
-
+               
                @Override /* RestMethodParam */
                public Object resolve(RestRequest req, RestResponse res) throws 
Exception {
-                       return req.getPathMatch().getRemainder();
+                       return ClassUtils.fromString(getTypeClass(), 
req.getPathMatch().getRemainder());
                }
        }
 
        static final class RestRequestPropertiesObject extends RestMethodParam {
 
                protected RestRequestPropertiesObject() {
-                       super(OTHER, null, RequestProperties.class);
+                       super(OTHER, RequestProperties.class);
                }
 
                @Override /* RestMethodParam */
@@ -1068,7 +1048,7 @@ class RestParamDefaults {
        static final class ResourceBundleObject extends RestMethodParam {
 
                protected ResourceBundleObject() {
-                       super(OTHER, null, ResourceBundle.class);
+                       super(OTHER, ResourceBundle.class);
                }
 
                @Override /* RestMethodParam */
@@ -1080,7 +1060,7 @@ class RestParamDefaults {
        static final class MessageBundleObject extends RestMethodParam {
 
                protected MessageBundleObject() {
-                       super(OTHER, null, MessageBundle.class);
+                       super(OTHER, MessageBundle.class);
                }
 
                @Override /* RestMethodParam */
@@ -1092,7 +1072,7 @@ class RestParamDefaults {
        static final class InputStreamObject extends RestMethodParam {
 
                protected InputStreamObject() {
-                       super(OTHER, null, InputStream.class);
+                       super(OTHER, InputStream.class);
                }
 
                @Override /* RestMethodParam */
@@ -1104,7 +1084,7 @@ class RestParamDefaults {
        static final class ServletInputStreamObject extends RestMethodParam {
 
                protected ServletInputStreamObject() {
-                       super(OTHER, null, ServletInputStream.class);
+                       super(OTHER, ServletInputStream.class);
                }
 
                @Override /* RestMethodParam */
@@ -1116,7 +1096,7 @@ class RestParamDefaults {
        static final class ReaderObject extends RestMethodParam {
 
                protected ReaderObject() {
-                       super(OTHER, null, Reader.class);
+                       super(OTHER, Reader.class);
                }
 
                @Override /* RestMethodParam */
@@ -1128,7 +1108,7 @@ class RestParamDefaults {
        static final class OutputStreamObject extends RestMethodParam {
 
                protected OutputStreamObject() {
-                       super(OTHER, null, OutputStream.class);
+                       super(OTHER, OutputStream.class);
                }
 
                @Override /* RestMethodParam */
@@ -1140,7 +1120,7 @@ class RestParamDefaults {
        static final class ServletOutputStreamObject extends RestMethodParam {
 
                protected ServletOutputStreamObject() {
-                       super(OTHER, null, ServletOutputStream.class);
+                       super(OTHER, ServletOutputStream.class);
                }
 
                @Override /* RestMethodParam */
@@ -1152,7 +1132,7 @@ class RestParamDefaults {
        static final class WriterObject extends RestMethodParam {
 
                protected WriterObject() {
-                       super(OTHER, null, Writer.class);
+                       super(OTHER, Writer.class);
                }
 
                @Override /* RestMethodParam */
@@ -1164,7 +1144,7 @@ class RestParamDefaults {
        static final class RequestHeadersObject extends RestMethodParam {
 
                protected RequestHeadersObject() {
-                       super(OTHER, null, RequestHeaders.class);
+                       super(OTHER, RequestHeaders.class);
                }
 
                @Override /* RestMethodParam */
@@ -1176,7 +1156,7 @@ class RestParamDefaults {
        static final class RequestQueryObject extends RestMethodParam {
 
                protected RequestQueryObject() {
-                       super(OTHER, null, RequestQuery.class);
+                       super(OTHER, RequestQuery.class);
                }
 
                @Override /* RestMethodParam */
@@ -1188,7 +1168,7 @@ class RestParamDefaults {
        static final class RequestFormDataObject extends RestMethodParam {
 
                protected RequestFormDataObject() {
-                       super(OTHER, null, RequestFormData.class);
+                       super(OTHER, RequestFormData.class);
                }
 
                @Override /* RestMethodParam */
@@ -1200,7 +1180,7 @@ class RestParamDefaults {
        static final class HttpMethodObject extends RestMethodParam {
 
                protected HttpMethodObject() {
-                       super(OTHER, null, HttpMethod.class);
+                       super(OTHER, HttpMethod.class);
                }
 
                @Override /* RestMethodParam */
@@ -1212,7 +1192,7 @@ class RestParamDefaults {
        static final class RestLoggerObject extends RestMethodParam {
 
                protected RestLoggerObject() {
-                       super(OTHER, null, RestLogger.class);
+                       super(OTHER, RestLogger.class);
                }
 
                @Override /* RestMethodParam */
@@ -1224,7 +1204,7 @@ class RestParamDefaults {
        static final class RestContextObject extends RestMethodParam {
 
                protected RestContextObject() {
-                       super(OTHER, null, RestContext.class);
+                       super(OTHER, RestContext.class);
                }
 
                @Override /* RestMethodParam */
@@ -1236,7 +1216,7 @@ class RestParamDefaults {
        static final class ParserObject extends RestMethodParam {
 
                protected ParserObject() {
-                       super(OTHER, null, Parser.class);
+                       super(OTHER, Parser.class);
                }
 
                @Override /* RestMethodParam */
@@ -1248,7 +1228,7 @@ class RestParamDefaults {
        static final class ReaderParserObject extends RestMethodParam {
 
                protected ReaderParserObject() {
-                       super(OTHER, null, ReaderParser.class);
+                       super(OTHER, ReaderParser.class);
                }
 
                @Override /* RestMethodParam */
@@ -1260,7 +1240,7 @@ class RestParamDefaults {
        static final class InputStreamParserObject extends RestMethodParam {
 
                protected InputStreamParserObject() {
-                       super(OTHER, null, InputStreamParser.class);
+                       super(OTHER, InputStreamParser.class);
                }
 
                @Override /* RestMethodParam */
@@ -1272,7 +1252,7 @@ class RestParamDefaults {
        static final class LocaleObject extends RestMethodParam {
 
                protected LocaleObject() {
-                       super(OTHER, null, Locale.class);
+                       super(OTHER, Locale.class);
                }
 
                @Override /* RestMethodParam */
@@ -1284,7 +1264,7 @@ class RestParamDefaults {
        static final class SwaggerObject extends RestMethodParam {
 
                protected SwaggerObject() {
-                       super(OTHER, null, Swagger.class);
+                       super(OTHER, Swagger.class);
                }
 
                @Override /* RestMethodParam */
@@ -1296,7 +1276,7 @@ class RestParamDefaults {
        static final class RequestPathMatchObject extends RestMethodParam {
 
                protected RequestPathMatchObject() {
-                       super(OTHER, null, RequestPathMatch.class);
+                       super(OTHER, RequestPathMatch.class);
                }
 
                @Override /* RestMethodParam */
@@ -1308,7 +1288,7 @@ class RestParamDefaults {
        static final class RequestBodyObject extends RestMethodParam {
 
                protected RequestBodyObject() {
-                       super(BODY, null, RequestBody.class);
+                       super(BODY, RequestBody.class);
                }
 
                @Override /* RestMethodParam */
@@ -1320,7 +1300,7 @@ class RestParamDefaults {
        static final class ConfigObject extends RestMethodParam {
 
                protected ConfigObject() {
-                       super(OTHER, null, Config.class);
+                       super(OTHER, Config.class);
                }
 
                @Override /* RestMethodParam */
@@ -1332,7 +1312,7 @@ class RestParamDefaults {
        static final class UriContextObject extends RestMethodParam {
 
                protected UriContextObject() {
-                       super(OTHER, null, UriContext.class);
+                       super(OTHER, UriContext.class);
                }
 
                @Override /* RestMethodParam */
@@ -1344,7 +1324,7 @@ class RestParamDefaults {
        static final class UriResolverObject extends RestMethodParam {
 
                protected UriResolverObject() {
-                       super(OTHER, null, UriResolver.class);
+                       super(OTHER, UriResolver.class);
                }
 
                @Override /* RestMethodParam */
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
index 9013981..9714566 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Body.java
@@ -21,13 +21,13 @@ import java.lang.annotation.*;
 import org.apache.juneau.rest.*;
 
 /**
- * Annotation that can be applied to a parameter of a {@link RestMethod 
@RestMethod} annotated method to identify it as the HTTP
+ * Annotation that can be applied to a parameter of a {@link RestMethod 
@RestMethod} annotated method or POJO class to identify it as the HTTP
  * request body converted to a POJO.
  * 
  * <h5 class='section'>Example:</h5>
  * <p class='bcode'>
  *     <ja>@RestMethod</ja>(name=<jsf>POST</jsf>)
- *     <jk>public void</jk> doPostPerson(RestRequest req, RestResponse res, 
<ja>@Body</ja> Person person) {
+ *     <jk>public void</jk> addPerson(<ja>@Body</ja> Person person) {
  *             ...
  *     }
  * </p>
@@ -36,7 +36,7 @@ import org.apache.juneau.rest.*;
  * This is functionally equivalent to the following code...
  * <p class='bcode'>
  *     <ja>@RestMethod</ja>(name=<jsf>POST</jsf>)
- *     <jk>public void</jk> doPostPerson(RestRequest req, RestResponse res) {
+ *     <jk>public void</jk> addPerson(RestRequest req, RestResponse res) {
  *             Person person = req.getBody().asType(Person.<jk>class</jk>);
  *             ...
  *     }
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 eb21173..17867f8 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
@@ -30,6 +30,9 @@ import org.apache.juneau.utils.*;
 import org.junit.*;
 import org.junit.runners.*;
 
+/**
+ * Tests the {@link BasicRestInfoProvider} class.
+ */
 @SuppressWarnings("javadoc")
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class BasicRestInfoProviderTest {
@@ -6615,12 +6618,6 @@ public class BasicRestInfoProviderTest {
                @RestMethod(name=GET,path="/schema5")
                public void ub05e(@Response UB05e b) {}
 
-               @RestMethod(name=GET,path="/schema6")
-               public void ub05f(@Response Integer b) {}
-
-               @RestMethod(name=GET,path="/schema7")
-               public void ub05g(@Response Boolean b) {}
-
                public static class UB07 {}
 
                @RestMethod(name=GET,path="/headers1")
@@ -6689,14 +6686,6 @@ public class BasicRestInfoProviderTest {
                assertObjectEquals("{type:'string'}", getSwagger(new 
UB()).getPaths().get("/schema5").get("get").getResponse(200).getSchema());
        }
        @Test
-       public void ub05f_Response_onParameter_schema6() throws Exception {
-               assertObjectEquals("{format:'int32',type:'integer'}", 
getSwagger(new 
UB()).getPaths().get("/schema6").get("get").getResponse(200).getSchema());
-       }
-       @Test
-       public void ub05g_Response_onParameter_schema7() throws Exception {
-               assertObjectEquals("{type:'boolean'}", getSwagger(new 
UB()).getPaths().get("/schema7").get("get").getResponse(200).getSchema());
-       }
-       @Test
        public void ub07_Response_onParameter_headers1() throws Exception {
                assertObjectEquals("{foo:{type:'a'}}", getSwagger(new 
UB()).getPaths().get("/headers1").get("get").getResponse(200).getHeaders());
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/BodyTest.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/BodyTest.java
index dd76d36..f4aadef 100644
--- 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/BodyTest.java
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/annotation/BodyTest.java
@@ -15,21 +15,31 @@ package org.apache.juneau.rest.annotation;
 import static org.apache.juneau.http.HttpMethodName.*;
 import static org.junit.Assert.*;
 
+import java.io.*;
+import java.util.*;
+
 import javax.servlet.http.*;
 
+import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.util.*;
 import org.junit.*;
 import org.junit.runners.*;
 
-@SuppressWarnings("javadoc")
+/**
+ * Tests the {@link Body} annotation.
+ */
+@SuppressWarnings({"javadoc","serial"})
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class BodyTest {
-
-       private void call(Object resource, HttpServletRequest req, 
HttpServletResponse res) throws Exception {
-               RestContext rc = RestContext.create(resource).build();
-               rc.getCallHandler().service(req, res);
+       
+       private static Map<Class<?>,RestContext> CONTEXTS = new HashMap<>();
+       
+       private static void call(Class<?> c, HttpServletRequest req, 
HttpServletResponse res) throws Exception {
+               if (! CONTEXTS.containsKey(c))
+                       CONTEXTS.put(c, 
RestContext.create(c.newInstance()).build());
+               CONTEXTS.get(c).getCallHandler().service(req, res);
        }
        
        
//-----------------------------------------------------------------------------------------------------------------
@@ -62,36 +72,86 @@ public class BodyTest {
                public A04 a04(@Body A04 b) {
                        return b;
                }
+
+               public static class A05 {
+                       String s;
+                       
+                       public A05(InputStream in) throws Exception {
+                               this.s = IOUtils.read(in);
+                       }
+                       
+                       @Override /* Object */
+                       public String toString() {
+                               return s;
+                       }
+               }
+               
+               @RestMethod(name=PUT, path="/inputStream")
+               public A05 a05(@Body A05 b) throws Exception {
+                       return b;
+               }
+
+               public static class A06 {
+                       private String s;
+                       
+                       public A06(Reader in) throws Exception {
+                               this.s = IOUtils.read(in);
+                       }
+                       
+                       @Override /* Object */
+                       public String toString() {
+                               return s;
+                       }
+               }
+               
+               @RestMethod(name=PUT, path="/reader")
+               public A06 a06(@Body A06 b) throws Exception {
+                       return b;
+               }
        }
        
        @Test
        public void a01_onParameter_string() throws Exception {
                MockServletRequest req = MockServletRequest.create("PUT", 
"/string").body("'foo'");
                MockServletResponse res = MockServletResponse.create();
-               call(new A(), req, res);
+               call(A.class, req, res);
                assertEquals("'foo'", res.getBodyAsString());
        }
        @Test
        public void a02_onParameter_integer() throws Exception {
                MockServletRequest req = MockServletRequest.create("PUT", 
"/integer").body("123");
                MockServletResponse res = MockServletResponse.create();
-               call(new A(), req, res);
+               call(A.class, req, res);
                assertEquals("123", res.getBodyAsString());
        }
        @Test
        public void a03_onParameter_boolean() throws Exception {
                MockServletRequest req = MockServletRequest.create("PUT", 
"/boolean").body("true");
                MockServletResponse res = MockServletResponse.create();
-               call(new A(), req, res);
+               call(A.class, req, res);
                assertEquals("true", res.getBodyAsString());
        }
        @Test
        public void a04_onParameter_bean() throws Exception {
                MockServletRequest req = MockServletRequest.create("PUT", 
"/bean").body("{f1:'a'}");
                MockServletResponse res = MockServletResponse.create();
-               call(new A(), req, res);
+               call(A.class, req, res);
                assertEquals("{f1:'a'}", res.getBodyAsString());
        }
+       @Test
+       public void a05_onParameter_inputStream() throws Exception {
+               MockServletRequest req = MockServletRequest.create("PUT", 
"/inputStream").body("a");
+               MockServletResponse res = MockServletResponse.create();
+               call(A.class, req, res);
+               assertEquals("'a'", res.getBodyAsString());
+       }
+       @Test
+       public void a06_onParameter_reader() throws Exception {
+               MockServletRequest req = MockServletRequest.create("PUT", 
"/reader").body("a");
+               MockServletResponse res = MockServletResponse.create();
+               call(A.class, req, res);
+               assertEquals("'a'", res.getBodyAsString());
+       }
 
        
//-----------------------------------------------------------------------------------------------------------------
        // @Body on POJO
@@ -128,20 +188,87 @@ public class BodyTest {
                public B02 b02(B02 b) {
                        return b;
                }
+
+               @Body
+               public static class B03 extends LinkedList<B02> {}
+               
+               @RestMethod(name=PUT, path="/beanList")
+               public B03 b03(B03 b) {
+                       return b;
+               }
+               
+               @Body 
+               public static class B04 {
+                       String s;
+                       
+                       public B04(InputStream in) throws Exception {
+                               this.s = IOUtils.read(in);
+                       }
+                       
+                       @Override /* Object */
+                       public String toString() {
+                               return s;
+                       }
+               }
+               
+               @RestMethod(name=PUT, path="/inputStream")
+               public B04 b04(B04 b) throws Exception {
+                       return b;
+               }
+               
+               @Body 
+               public static class B05 {
+                       private String s;
+                       
+                       public B05(Reader in) throws Exception {
+                               this.s = IOUtils.read(in);
+                       }
+                       
+                       @Override /* Object */
+                       public String toString() {
+                               return s;
+                       }
+               }
+               
+               @RestMethod(name=PUT, path="/reader")
+               public B05 b05(B05 b) throws Exception {
+                       return b;
+               }
        }
        
        @Test
-       public void a01_onPojo_string() throws Exception {
-               MockServletRequest req = MockServletRequest.create("PUT", 
"/string").body("'foo'");
+       public void b01_onPojo_string() throws Exception {
+               MockServletRequest req = MockServletRequest.create(PUT, 
"/string").body("'foo'");
                MockServletResponse res = MockServletResponse.create();
-               call(new B(), req, res);
+               call(B.class, req, res);
                assertEquals("'foo'", res.getBodyAsString());
        }
        @Test
-       public void a04_onPojo_bean() throws Exception {
-               MockServletRequest req = MockServletRequest.create("PUT", 
"/bean").body("{f1:'a'}");
+       public void b02_onPojo_bean() throws Exception {
+               MockServletRequest req = MockServletRequest.create(PUT, 
"/bean").body("{f1:'a'}");
                MockServletResponse res = MockServletResponse.create();
-               call(new B(), req, res);
+               call(B.class, req, res);
                assertEquals("{f1:'a'}", res.getBodyAsString());
        }
+       @Test
+       public void b03_onPojo_beanList() throws Exception {
+               MockServletRequest req = MockServletRequest.create(PUT, 
"/beanList").body("[{f1:'a'}]");
+               MockServletResponse res = MockServletResponse.create();
+               call(B.class, req, res);
+               assertEquals("[{f1:'a'}]", res.getBodyAsString());
+       }
+       @Test
+       public void b04_onPojo_inputStream() throws Exception {
+               MockServletRequest req = MockServletRequest.create(PUT, 
"/inputStream").body("a");
+               MockServletResponse res = MockServletResponse.create();
+               call(B.class, req, res);
+               assertEquals("'a'", res.getBodyAsString());
+       }
+       @Test
+       public void b05_onPojo_reader() throws Exception {
+               MockServletRequest req = MockServletRequest.create(PUT, 
"/reader").body("a");
+               MockServletResponse res = MockServletResponse.create();
+               call(B.class, req, res);
+               assertEquals("'a'", res.getBodyAsString());
+       }
 }

-- 
To stop receiving notification emails like this one, please contact
jamesbog...@apache.org.

Reply via email to