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 73d4268  Convert ReaderResource/StreamResource to @Response beans.
73d4268 is described below

commit 73d4268927fc1fac61782c59e78a54635183a14f
Author: JamesBognar <jamesbog...@apache.org>
AuthorDate: Sat Aug 11 20:55:38 2018 -0400

    Convert ReaderResource/StreamResource to @Response beans.
---
 .../org/apache/juneau/encoders/EncoderGroup.java   |   5 +
 .../juneau/http/annotation/ResponseBody.java       |  26 ++++-
 .../httppart/OpenApiPartSerializerSession.java     |   5 +
 .../juneau/httppart/bean/ResponseBeanMeta.java     |  18 +++-
 .../java/org/apache/juneau/parser/ParserGroup.java |   5 +
 .../apache/juneau/serializer/SerializerGroup.java  |   5 +
 .../java/org/apache/juneau/utils/ZipFileList.java  |  33 ++++++-
 juneau-doc/src/main/javadoc/overview.html          |   2 +-
 .../apache/juneau/rest/BasicRestCallHandler.java   |  10 +-
 .../org/apache/juneau/rest/ResponseHandler.java    |   5 -
 .../java/org/apache/juneau/rest/RestContext.java   |  42 ++++++--
 .../org/apache/juneau/rest/RestContextBuilder.java |   2 -
 .../java/org/apache/juneau/rest/RestRequest.java   |  28 +++---
 .../java/org/apache/juneau/rest/RestResponse.java  |   8 +-
 .../apache/juneau/rest/helper/ReaderResource.java  |   9 +-
 .../apache/juneau/rest/helper/StreamResource.java  |   9 +-
 .../juneau/rest/reshandlers/DefaultHandler.java    |  26 ++++-
 .../juneau/rest/reshandlers/StreamableHandler.java |  58 -----------
 .../juneau/rest/reshandlers/WritableHandler.java   |  58 -----------
 .../reshandlers/ZipFileListResponseHandler.java    |  64 ------------
 .../jueau/rest/helper/ReaderResourceTest.java      | 109 +++++++++++++++++++++
 .../jueau/rest/helper/StreamResourceTest.java      | 109 +++++++++++++++++++++
 22 files changed, 400 insertions(+), 236 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
index 066432c..6030c1b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/encoders/EncoderGroup.java
@@ -56,6 +56,11 @@ import org.apache.juneau.http.*;
  */
 public final class EncoderGroup {
 
+       /**
+        * A default encoder group consisting of identity and G-Zip encoding.
+        */
+       public static final EncoderGroup DEFAULT = 
create().append(IdentityEncoder.class, GzipEncoder.class).build();
+
        // Maps Accept-Encoding headers to matching encoders.
        private final ConcurrentHashMap<String,EncoderMatch> cache = new 
ConcurrentHashMap<>();
 
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 ed5536f..d4fbc3b 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
@@ -15,6 +15,7 @@ package org.apache.juneau.http.annotation;
 import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
+import java.io.*;
 import java.lang.annotation.*;
 
 /**
@@ -34,6 +35,13 @@ import java.lang.annotation.*;
  * <p>
  * On {@link Response @Response}-annotated classes, this method can be used to 
denote a POJO to use as the response.
  *
+ * <p>
+ * The method must be public and be one of the following:
+ * <ul>
+ *     <li>A public no-arg method with a POJO return type.
+ *     <li>A public one-arg method with a <jk>void</jk> return type that takes 
in a {@link Reader} or {@link OutputStream}.
+ * </ul>
+ *
  * <h5 class='section'>Example:</h5>
  * <p class='bcode w800'>
  *     <ja>@RestMethod</ja>
@@ -53,8 +61,22 @@ import java.lang.annotation.*;
  *     }
  * </p>
  *
- * <p>
- * The method being annotated must be public.
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode w800'>
+ *     <ja>@Response</ja>
+ *     <jk>public class</jk> MyCustomJsonResponse {
+ *
+ *             <ja>@ResponseHeader</ja>(<js>"Content-Type"</js>)
+ *             <jk>public</jk> String getContentType() {
+ *                     <jk>return</jk> <js>"application/json"</js>;
+ *             }
+ *
+ *             <ja>@ResponseBody</ja>
+ *             <jk>public void</jk> writeTo(Writer out) {
+ *                     out.write(<js>"{'foo':'bar'}"</js>);
+ *             }
+ *     }
+ * </p>
  *
  * <h5 class='section'>See Also:</h5>
  * <ul>
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializerSession.java
index 14829b3..690ab8a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/OpenApiPartSerializerSession.java
@@ -138,6 +138,11 @@ public class OpenApiPartSerializerSession extends 
UonPartSerializerSession {
                        type = schema.getParsedType();
                }
 
+               if (type.isUri()) {
+                       value = getUriResolver().resolve(value);
+                       type = string();
+               }
+
                if (value != null) {
 
                        if (t == STRING) {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
index 0554fd8..70cb830 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
@@ -15,6 +15,7 @@ package org.apache.juneau.httppart.bean;
 import static org.apache.juneau.internal.ClassFlags.*;
 import static org.apache.juneau.internal.ClassUtils.*;
 
+import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
@@ -153,7 +154,7 @@ public class ResponseBeanMeta {
                        Class<?> c = ClassUtils.toClass(t);
                        this.cm = BeanContext.DEFAULT.getClassMeta(c);
                        for (Method m : ClassUtils.getAllMethods(c, false)) {
-                               if (isAll(m, PUBLIC, HAS_NO_ARGS)) {
+                               if (isAll(m, PUBLIC)) {
                                        if (hasAnnotation(ResponseHeader.class, 
m)) {
                                                if 
(m.getParameterTypes().length != 0)
                                                        throw new 
InvalidAnnotationException("@ResponseHeader annotation on method cannot have 
arguments.  Method=''{0}''", m);
@@ -176,11 +177,18 @@ public class ResponseBeanMeta {
                                                statusMethod = m;
                                        }
                                        if (hasAnnotation(ResponseBody.class, 
m)) {
-                                               if 
(m.getParameterTypes().length != 0)
+                                               Class<?>[] pt = 
m.getParameterTypes();
+                                               if (pt.length == 0) {
+                                                       Class<?> rt = 
m.getReturnType();
+                                                       if (rt == void.class)
+                                                               throw new 
InvalidAnnotationException("Invalid return type for @ResponseBody annotation on 
method.  Method=''{0}''", m);
+                                               } else if (pt.length == 1) {
+                                                       Class<?> rt = pt[0];
+                                                       if (rt != 
OutputStream.class && rt != Writer.class)
+                                                               throw new 
InvalidAnnotationException("Invalid return type for @ResponseBody annotation on 
method.  Method=''{0}''", m);
+                                               } else {
                                                        throw new 
InvalidAnnotationException("@ResponseBody annotation on method cannot have 
arguments.  Method=''{0}''", m);
-                                               Class<?> rt = m.getReturnType();
-                                               if (rt == void.class)
-                                                       throw new 
InvalidAnnotationException("Invalid return type for @ResponseBody annotation on 
method.  Method=''{0}''", m);
+                                               }
                                                bodyMethod = m;
                                        }
                                        if (hasAnnotation(Body.class, m))
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserGroup.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserGroup.java
index 7be9fc9..cac2540 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserGroup.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserGroup.java
@@ -73,6 +73,11 @@ import org.apache.juneau.http.*;
  */
 public final class ParserGroup extends BeanContext {
 
+       /**
+        * An unmodifiable empty parser group.
+        */
+       public static final ParserGroup EMPTY = create().build();
+
        // Maps Content-Type headers to matches.
        private final ConcurrentHashMap<String,ParserMatch> cache = new 
ConcurrentHashMap<>();
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
index 904386b..695bbb8 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
@@ -65,6 +65,11 @@ import org.apache.juneau.http.*;
  */
 public final class SerializerGroup extends BeanContext {
 
+       /**
+        * An unmodifiable empty serializer group.
+        */
+       public static final SerializerGroup EMPTY = create().build();
+
        // Maps Accept headers to matching serializers.
        private final ConcurrentHashMap<String,SerializerMatch> cache = new 
ConcurrentHashMap<>();
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ZipFileList.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ZipFileList.java
index a12183f..82f3d14 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ZipFileList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ZipFileList.java
@@ -16,6 +16,10 @@ import java.io.*;
 import java.util.*;
 import java.util.zip.*;
 
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.annotation.*;
+
 /**
  * Utility class for representing the contents of a zip file as a list of 
entries whose contents don't resolve until
  * serialization time.
@@ -25,13 +29,40 @@ import java.util.zip.*;
  * REST methods can easily create ZIP file responses by simply returning 
instances of this class.
  */
 @SuppressWarnings("serial")
-public class ZipFileList extends LinkedList<ZipFileList.ZipFileEntry> {
+@Response
+public class ZipFileList extends LinkedList<ZipFileList.ZipFileEntry> 
implements Streamable {
 
        /**
         * The name of the zip file.
         */
        public final String fileName;
 
+       @Header("Content-Type")
+       @Override /* Streamable */
+       public MediaType getMediaType() {
+               return MediaType.forString("application/zip");
+       }
+
+       /**
+        * Returns the value for the <code>Content-Disposition</code> header.
+        *
+        * @return The value for the <code>Content-Disposition</code> header.
+        */
+       @Header("Content-Disposition")
+       public String getContentDisposition() {
+               return "attachment;filename=" + fileName;
+       }
+
+       @ResponseBody
+       @Override /* Streamable */
+       public void streamTo(OutputStream os) throws IOException {
+               try (ZipOutputStream zos = new ZipOutputStream(os)) {
+                       for (ZipFileEntry e : this)
+                               e.write(zos);
+               }
+               os.flush();
+       }
+
        /**
         * Constructor.
         *
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 4fdb959..3bc25cf 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -16246,7 +16246,7 @@ TODO(7.2.0)
        This can only be applied to parameters of the {@link 
org.apache.juneau.Value} class with an {@link java.lang.Integer} type.
 </p>
 
-<h5 class='section'>Examples:</h5>
+<h5 class='figure'>Examples:</h5>
 <p class='bpcode w800'>
        <jc>// Defined on parameter.</jc>
        <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/user/login"</js>)
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index c11dccc..409c33c 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -23,6 +23,7 @@ import java.util.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 
+import org.apache.juneau.rest.RestContext.*;
 import org.apache.juneau.rest.exception.*;
 import org.apache.juneau.rest.helper.*;
 import org.apache.juneau.rest.util.*;
@@ -145,10 +146,13 @@ public class BasicRestCallHandler implements 
RestCallHandler {
                        StreamResource r = null;
                        if (pathInfo != null) {
                                String p = pathInfo.substring(1);
-                               if (context.isStaticFile(p))
-                                       r = context.resolveStaticFile(p);
-                               else if (p.equals("favicon.ico"))
+                               if (context.isStaticFile(p)) {
+                                       StaticFile sf = 
context.resolveStaticFile(p);
+                                       r = sf.resource;
+                                       res.setResponseMeta(sf.meta);
+                               } else if (p.equals("favicon.ico")) {
                                        res.setOutput(null);
+                               }
                        }
 
                        if (r != null) {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
index b3f725f..561f95a 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
@@ -16,7 +16,6 @@ import java.io.*;
 
 import javax.servlet.http.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.reshandlers.*;
 
@@ -46,10 +45,6 @@ import org.apache.juneau.rest.reshandlers.*;
  *     <li class='jc'>
  *             {@link InputStreamHandler} - Pipes the output of {@link 
InputStream InputStreams} to the response output
  *             stream ({@link RestResponse#getOutputStream()}).
- *     <li class='jc'>
- *             {@link WritableHandler} - Handles {@link Writable} objects.
- *     <li class='jc'>
- *             {@link StreamableHandler} - Handles {@link Streamable} objects.
  * </ul>
  *
  * <p>
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 303dc5a..2cc1eed 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -39,6 +39,7 @@ import org.apache.juneau.htmlschema.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.bean.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.jsonschema.*;
@@ -2150,11 +2151,8 @@ public final class RestContext extends BeanContext {
         * <p>
         * By default, the following response handlers are provided 
out-of-the-box:
         * <ul>
-        *      <li class='jc'>{@link StreamableHandler} - {@link Streamable} 
objects.
-        *      <li class='jc'>{@link WritableHandler} - {@link Writable} 
objects.
         *      <li class='jc'>{@link ReaderHandler} - {@link Reader} objects.
         *      <li class='jc'>{@link InputStreamHandler} - {@link InputStream} 
objects.
-        *      <li class='jc'>{@link ZipFileListResponseHandler} - {@link 
ZipFileList} objects.
         *      <li class='jc'>{@link DefaultHandler} - All other POJOs.
         * </ul>
         *
@@ -2846,7 +2844,7 @@ public final class RestContext extends BeanContext {
                destroyMethodParams;
 
        // In-memory cache of images and stylesheets in the 
org.apache.juneau.rest.htdocs package.
-       private final Map<String,StreamResource> staticFilesCache = new 
ConcurrentHashMap<>();
+       private final Map<String,StaticFile> staticFilesCache = new 
ConcurrentHashMap<>();
 
        private final ClasspathResourceManager staticResourceManager;
        private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes 
= new ConcurrentHashMap<>();
@@ -3354,15 +3352,16 @@ public final class RestContext extends BeanContext {
         * </ul>
         *
         * @param pathInfo The unencoded path info.
-        * @return The resource, or <jk>null</jk> if the resource could not be 
resolved.
+        * @return The wrapped resource, never <jk>null</jk>.
         * @throws NotFound Invalid path.
         * @throws IOException
         */
-       public StreamResource resolveStaticFile(String pathInfo) throws 
NotFound, IOException {
+       protected StaticFile resolveStaticFile(String pathInfo) throws 
NotFound, IOException {
                if (! staticFilesCache.containsKey(pathInfo)) {
                        String p = urlDecode(trimSlashes(pathInfo));
                        if (p.indexOf("..") != -1)
                                throw new NotFound("Invalid path");
+                       StreamResource sr = null;
                        for (StaticFileMapping sfm : staticFiles) {
                                String path = sfm.path;
                                if (p.startsWith(path)) {
@@ -3375,20 +3374,43 @@ public final class RestContext extends BeanContext {
                                                                String name = 
(i == -1 ? p2 : p2.substring(i+1));
                                                                String 
mediaType = mimetypesFileTypeMap.getContentType(name);
                                                                
Map<String,Object> responseHeaders = sfm.responseHeaders != null ? 
sfm.responseHeaders : staticFileResponseHeaders;
-                                                               StreamResource 
sr = new StreamResource(MediaType.forString(mediaType), responseHeaders, is);
-                                                               if 
(useClasspathResourceCaching)
-                                                                       
staticFilesCache.put(pathInfo, sr);
-                                                               return sr;
+                                                               sr = new 
StreamResource(MediaType.forString(mediaType), responseHeaders, is);
+                                                               break;
                                                        }
                                                }
                                        }
                                }
                        }
+                       StaticFile sf = new StaticFile(sr);
+                       if (useClasspathResourceCaching) {
+                               if (staticFilesCache.size() > 100)
+                                       staticFilesCache.clear();
+                               staticFilesCache.put(pathInfo, sf);
+                       }
+                       return sf;
                }
                return staticFilesCache.get(pathInfo);
        }
 
        /**
+        * A cached static file instance.
+        */
+       protected class StaticFile {
+               StreamResource resource;
+               ResponseBeanMeta meta;
+
+               /**
+                * Constructor.
+                *
+                * @param resource
+                */
+               protected StaticFile(StreamResource resource) {
+                       this.resource = resource;
+                       this.meta = resource == null ? null : 
ResponseBeanMeta.create(resource.getClass(), getPropertyStore());
+               }
+       }
+
+       /**
         * Same as {@link Class#getResourceAsStream(String)} except if it 
doesn't find the resource on this class, searches
         * up the parent hierarchy chain.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index f6a8505..271c409 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -240,8 +240,6 @@ public class RestContextBuilder extends BeanContextBuilder 
implements ServletCon
                        }
 
                        responseHandlers(
-                               StreamableHandler.class,
-                               WritableHandler.class,
                                ReaderHandler.class,
                                InputStreamHandler.class,
                                DefaultHandler.class
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 4c21966..8c865ea 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -371,7 +371,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return The set of media types registered in the serializer group of 
this request.
         */
        public List<MediaType> getProduces() {
-               return restJavaMethod.supportedAcceptTypes;
+               return restJavaMethod == null ? 
Collections.<MediaType>emptyList() : restJavaMethod.supportedAcceptTypes;
        }
 
        /**
@@ -380,7 +380,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return The set of media types registered in the parser group of 
this request.
         */
        public List<MediaType> getConsumes() {
-               return restJavaMethod.supportedContentTypes;
+               return restJavaMethod == null ? 
Collections.<MediaType>emptyList() : restJavaMethod.supportedContentTypes;
        }
 
        /**
@@ -406,8 +406,10 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                                if (i > 0)
                                        charset = h.substring(i+9).trim();
                        }
-                       if (charset == null)
+                       if (charset == null && restJavaMethod != null)
                                charset = restJavaMethod.defaultCharset;
+                       if (charset == null)
+                               charset = "UTF-8";
                        if (! Charset.isSupported(charset))
                                throw new UnsupportedMediaType("Unsupported 
charset in header ''Content-Type'': ''{0}''", h);
                }
@@ -561,7 +563,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
        public RequestFormData getFormData() throws InternalServerError {
                try {
                        if (formData == null) {
-                               formData = new RequestFormData(this, 
restJavaMethod.partParser);
+                               formData = new RequestFormData(this, 
restJavaMethod == null ? OpenApiPartParser.DEFAULT : restJavaMethod.partParser);
                                if (! body.isLoaded()) {
                                        formData.putAll(getParameterMap());
                                } else {
@@ -572,7 +574,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
                                        }
                                }
                        }
-                       formData.addDefault(restJavaMethod.defaultFormData);
+                       formData.addDefault(restJavaMethod == null ? null : 
restJavaMethod.defaultFormData);
                        return formData;
                } catch (Exception e) {
                        throw new InternalServerError(e);
@@ -1057,7 +1059,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return The serializers associated with this request.
         */
        public SerializerGroup getSerializers() {
-               return restJavaMethod.serializers;
+               return restJavaMethod == null ? SerializerGroup.EMPTY : 
restJavaMethod.serializers;
        }
 
        /**
@@ -1071,7 +1073,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return The parsers associated with this request.
         */
        public ParserGroup getParsers() {
-               return restJavaMethod.parsers;
+               return restJavaMethod == null ? ParserGroup.EMPTY : 
restJavaMethod.parsers;
        }
 
        /**
@@ -1080,7 +1082,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return The part serializer associated with this request.
         */
        public HttpPartParser getPartParser() {
-               return restJavaMethod.partParser;
+               return restJavaMethod == null ? OpenApiPartParser.DEFAULT : 
restJavaMethod.partParser;
        }
 
        /**
@@ -1089,7 +1091,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return The part serializer associated with this request.
         */
        public HttpPartSerializer getPartSerializer() {
-               return restJavaMethod.partSerializer;
+               return restJavaMethod == null ? OpenApiPartSerializer.DEFAULT : 
restJavaMethod.partSerializer;
        }
 
        /**
@@ -1406,7 +1408,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         *      Never <jk>null</jk>.
         */
        public Map<String,Widget> getWidgets() {
-               return restJavaMethod.widgets;
+               return restJavaMethod == null ? 
Collections.<String,Widget>emptyMap() : restJavaMethod.widgets;
        }
 
        /**
@@ -1602,7 +1604,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return Metadata about the specified response object, or 
<jk>null</jk> if it's not annotated with {@link Response @Response}.
         */
        public ResponseBeanMeta getResponseBeanMeta(Object o) {
-               return restJavaMethod.getResponseBeanMeta(o);
+               return restJavaMethod == null ? null : 
restJavaMethod.getResponseBeanMeta(o);
        }
 
        /**
@@ -1612,7 +1614,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return Metadata about the specified response object, or 
<jk>null</jk> if it's not annotated with {@link ResponseHeader @ResponseHeader}.
         */
        public ResponsePartMeta getResponseHeaderMeta(Object o) {
-               return restJavaMethod.getResponseHeaderMeta(o);
+               return restJavaMethod == null ? null : 
restJavaMethod.getResponseHeaderMeta(o);
        }
 
        /**
@@ -1622,7 +1624,7 @@ public final class RestRequest extends 
HttpServletRequestWrapper {
         * @return Metadata about the specified response object, or 
<jk>null</jk> if it's not annotated with {@link ResponseBody @ResponseBody}.
         */
        public ResponsePartMeta getResponseBodyMeta(Object o) {
-               return restJavaMethod.getResponseBodyMeta(o);
+               return restJavaMethod == null ? null : 
restJavaMethod.getResponseBodyMeta(o);
        }
 
        
//--------------------------------------------------------------------------------
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index 88cde04..eefc5d0 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -134,7 +134,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
         * @return The serializer group for the response.
         */
        public SerializerGroup getSerializers() {
-               return restJavaMethod.serializers;
+               return restJavaMethod == null ? SerializerGroup.EMPTY : 
restJavaMethod.serializers;
        }
 
        /**
@@ -143,7 +143,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
         * @return The set of media types registered in the parser group of 
this request.
         */
        public List<MediaType> getSupportedMediaTypes() {
-               return restJavaMethod.supportedAcceptTypes;
+               return restJavaMethod == null ? 
Collections.<MediaType>emptyList() : restJavaMethod.supportedAcceptTypes;
        }
 
        /**
@@ -154,7 +154,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
         * @throws RestServletException
         */
        public List<String> getSupportedEncodings() throws RestServletException 
{
-               return restJavaMethod.encoders.getSupportedEncodings();
+               return restJavaMethod == null ? Collections.<String>emptyList() 
: restJavaMethod.encoders.getSupportedEncodings();
        }
 
        /**
@@ -360,7 +360,7 @@ public final class RestResponse extends 
HttpServletResponseWrapper {
        public FinishableServletOutputStream getNegotiatedOutputStream() throws 
NotAcceptable, IOException {
                if (os == null) {
                        Encoder encoder = null;
-                       EncoderGroup encoders = restJavaMethod.encoders;
+                       EncoderGroup encoders = restJavaMethod == null ? 
EncoderGroup.DEFAULT : restJavaMethod.encoders;
 
                        String ae = request.getHeader("Accept-Encoding");
                        if (! (ae == null || ae.isEmpty())) {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/ReaderResource.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/ReaderResource.java
index 65f3b43..5b87635 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/ReaderResource.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/ReaderResource.java
@@ -20,7 +20,7 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.*;
-import org.apache.juneau.rest.reshandlers.*;
+import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.svl.*;
 
 /**
@@ -28,8 +28,7 @@ import org.apache.juneau.svl.*;
  * HTTP response headers.
  *
  * <p>
- * This class is handled special by the {@link WritableHandler} class.
- * <br>This allows these objects to be returned as responses by REST methods.
+ * <br>These objects can be returned as responses by REST methods.
  *
  * <p>
  * <l>ReaderResources</l> are meant to be thread-safe and reusable objects.
@@ -43,6 +42,7 @@ import org.apache.juneau.svl.*;
  *     <li class='link'><a class="doclink" 
href="../../../../../overview-summary.html#juneau-rest-server.RestMethod.ReaderResource">Overview
 &gt; juneau-rest-server &gt; ReaderResource</a>
  * </ul>
  */
+@Response
 public class ReaderResource implements Writable {
 
        private final MediaType mediaType;
@@ -109,10 +109,12 @@ public class ReaderResource implements Writable {
         *      <br>An unmodifiable map.
         *      <br>Never <jk>null</jk>.
         */
+       @ResponseHeader("*")
        public Map<String,Object> getHeaders() {
                return headers;
        }
 
+       @ResponseBody
        @Override /* Writeable */
        public Writer writeTo(Writer w) throws IOException {
                for (String s : contents) {
@@ -124,6 +126,7 @@ public class ReaderResource implements Writable {
                return w;
        }
 
+       @ResponseHeader("Content-Type")
        @Override /* Writeable */
        public MediaType getMediaType() {
                return mediaType;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/StreamResource.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/StreamResource.java
index b29d971..177c4cf 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/StreamResource.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/StreamResource.java
@@ -20,14 +20,13 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.*;
-import org.apache.juneau.rest.reshandlers.*;
+import org.apache.juneau.http.annotation.*;
 
 /**
  * Represents the contents of a byte stream file with convenience methods for 
adding HTTP response headers.
  *
  * <p>
- * This class is handled special by the {@link StreamableHandler} class.
- * <br>This allows these objects to be returned as responses by REST methods.
+ * <br>These objects can to be returned as responses by REST methods.
  *
  * <p>
  * <l>StreamResources</l> are meant to be thread-safe and reusable objects.
@@ -41,6 +40,7 @@ import org.apache.juneau.rest.reshandlers.*;
  *     <li class='link'><a class="doclink" 
href="../../../../../overview-summary.html#juneau-rest-server.RestMethod.StreamResource">Overview
 &gt; juneau-rest-server &gt; @RestMethod &gt; StreamResource</a>
  * </ul>
  */
+@Response
 public class StreamResource implements Streamable {
 
        private final MediaType mediaType;
@@ -107,10 +107,12 @@ public class StreamResource implements Streamable {
         *      <br>An unmodifiable map.
         *      <br>Never <jk>null</jk>.
         */
+       @ResponseHeader("*")
        public Map<String,Object> getHeaders() {
                return headers;
        }
 
+       @ResponseBody
        @Override /* Streamable */
        public void streamTo(OutputStream os) throws IOException {
                for (byte[] b : contents)
@@ -118,6 +120,7 @@ public class StreamResource implements Streamable {
                os.flush();
        }
 
+       @ResponseHeader("Content-Type")
        @Override /* Streamable */
        public MediaType getMediaType() {
                return mediaType;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
index 4eded8d..5e444c7 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
@@ -13,10 +13,10 @@
 package org.apache.juneau.rest.reshandlers;
 
 import static org.apache.juneau.internal.ObjectUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
 import java.io.*;
 import java.lang.reflect.*;
-import java.net.*;
 import java.util.*;
 
 import org.apache.juneau.*;
@@ -78,9 +78,18 @@ public class DefaultHandler implements ResponseHandler {
                        for (ResponseBeanPropertyMeta hm : 
rm.getHeaderMethods()) {
                                try {
                                        Object ho = hm.getGetter().invoke(o);
-                                       if (ho instanceof URI)
-                                               ho = 
req.getUriResolver().resolve(ho);
-                                       res.setHeader(new 
HttpPart(hm.getPartName(), HttpPartType.HEADER, hm.getSchema(), 
firstNonNull(hm.getSerializer(), req.getPartSerializer()), 
req.getSerializerSessionArgs(), ho));
+                                       String n = hm.getPartName();
+                                       if ("*".equals(n) && ho instanceof Map) 
{
+                                               @SuppressWarnings("rawtypes") 
Map m = (Map)ho;
+                                               for (Object key : m.keySet()) {
+                                                       String k = 
asString(key);
+                                                       Object v = m.get(key);
+                                                       HttpPartSchema s = 
hm.getSchema().getProperty(k);
+                                                       res.setHeader(new 
HttpPart(k, HttpPartType.HEADER, s, firstNonNull(hm.getSerializer(), 
req.getPartSerializer()), req.getSerializerSessionArgs(), v));
+                                               }
+                                       } else {
+                                               res.setHeader(new HttpPart(n, 
HttpPartType.HEADER, hm.getSchema(), firstNonNull(hm.getSerializer(), 
req.getPartSerializer()), req.getSerializerSessionArgs(), ho));
+                                       }
                                } catch (Exception e) {
                                        throw new InternalServerError(e, "Could 
not set header ''{0}''", hm.getPartName());
                                }
@@ -92,6 +101,15 @@ public class DefaultHandler implements ResponseHandler {
 
                        if (m != null) {
                                try {
+                                       Class<?>[] pt = m.getParameterTypes();
+                                       if (pt.length == 1) {
+                                               Class<?> ptt = pt[0];
+                                               if (ptt == OutputStream.class)
+                                                       m.invoke(o, 
res.getOutputStream());
+                                               else if (ptt == Writer.class)
+                                                       m.invoke(o, 
res.getWriter());
+                                               return true;
+                                       }
                                        o = m.invoke(o);
                                        schema = rm.getSchema();
                                        usePartSerializer |= 
schema.isUsePartSerializer();
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/StreamableHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/StreamableHandler.java
deleted file mode 100644
index 80cf26c..0000000
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/StreamableHandler.java
+++ /dev/null
@@ -1,58 +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.rest.reshandlers;
-
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.io.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.rest.*;
-import org.apache.juneau.rest.helper.*;
-
-/**
- * Response handler for {@link Writable} and {@link ReaderResource} objects.
- *
- * <p>
- * Uses the {@link Writable#writeTo(Writer)} method to send the contents to the
- * {@link RestResponse#getNegotiatedWriter()} writer.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- *     <li class='link'><a class="doclink" 
href="../../../../../overview-summary.html#juneau-rest-server.RestMethod.MethodReturnTypes">Overview
 &gt; juneau-rest-server &gt; Method Return Types</a>
- * </ul>
- */
-public final class StreamableHandler implements ResponseHandler {
-
-       @Override /* ResponseHandler */
-       public boolean handle(RestRequest req, RestResponse res) throws 
IOException, RestException {
-               if (res.isOutputType(Streamable.class)) {
-                       if (res.isOutputType(StreamResource.class)) {
-                               StreamResource r = 
res.getOutput(StreamResource.class);
-                               MediaType mediaType = r.getMediaType();
-                               if (mediaType != null)
-                                       
res.setContentType(mediaType.toString());
-                               for (Map.Entry<String,Object> h : 
r.getHeaders().entrySet())
-                                       res.setHeader(h.getKey(), 
asString(h.getValue()));
-                       }
-                       try (OutputStream os = res.getOutputStream()) {
-                               res.getOutput(Streamable.class).streamTo(os);
-                       }
-                       return true;
-               }
-               return false;
-       }
-}
-
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/WritableHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/WritableHandler.java
deleted file mode 100644
index fc926fd..0000000
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/WritableHandler.java
+++ /dev/null
@@ -1,58 +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.rest.reshandlers;
-
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.io.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.rest.*;
-import org.apache.juneau.rest.exception.*;
-import org.apache.juneau.rest.helper.*;
-
-/**
- * Response handler for {@link Writable} and {@link ReaderResource} objects.
- *
- * <p>
- * Uses the {@link Writable#writeTo(Writer)} method to send the contents to 
the {@link RestResponse#getNegotiatedWriter()} writer.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- *     <li class='link'><a class="doclink" 
href="../../../../../overview-summary.html#juneau-rest-server.RestMethod.MethodReturnTypes">Overview
 &gt; juneau-rest-server &gt; Method Return Types</a>
- * </ul>
- */
-public final class WritableHandler implements ResponseHandler {
-
-       @Override /* ResponseHandler */
-       public boolean handle(RestRequest req, RestResponse res) throws 
IOException, NotAcceptable, RestException {
-               if (res.isOutputType(Writable.class)) {
-                       if (res.isOutputType(ReaderResource.class)) {
-                               ReaderResource r = 
res.getOutput(ReaderResource.class);
-                               MediaType mediaType = r.getMediaType();
-                               if (mediaType != null)
-                                       
res.setContentType(mediaType.toString());
-                               for (Map.Entry<String,Object> h : 
r.getHeaders().entrySet())
-                                       res.setHeader(h.getKey(), 
asString(h.getValue()));
-                       }
-                       try (Writer w = res.getNegotiatedWriter()) {
-                               res.getOutput(Writable.class).writeTo(w);
-                       }
-                       return true;
-               }
-               return false;
-       }
-}
-
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/ZipFileListResponseHandler.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/ZipFileListResponseHandler.java
deleted file mode 100644
index 3fce703..0000000
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/ZipFileListResponseHandler.java
+++ /dev/null
@@ -1,64 +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.rest.reshandlers;
-
-import java.io.*;
-import java.util.zip.*;
-
-import org.apache.juneau.rest.*;
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.utils.*;
-import org.apache.juneau.utils.ZipFileList.*;
-
-/**
- * Response handler for ZipFileList objects.
- *
- * <p>
- * Can be associated with a REST resource using the {@link 
RestResource#responseHandlers} annotation.
- *
- * <p>
- * Sets the following headers:
- * <ul class='spaced-list'>
- *     <li>
- *             <code>Content-Type</code> - <code>application/zip</code>
- *     <li>
- *             <code>Content-Disposition=attachment;filename=X</code> - Sets X 
to the file name passed in through the
- *             constructor {@link ZipFileList#ZipFileList(String)}.
- * </ul>
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- *     <li class='link'><a class="doclink" 
href="../../../../../overview-summary.html#juneau-rest-server.RestMethod.MethodReturnTypes">Overview
 &gt; juneau-rest-server &gt; Method Return Types</a>
- * </ul>
- */
-public class ZipFileListResponseHandler implements ResponseHandler {
-
-       @Override /* ResponseHandler */
-       public boolean handle(RestRequest req, RestResponse res) throws 
IOException, RestException {
-               if (res.isOutputType(ZipFileList.class)) {
-                       ZipFileList m = res.getOutput(ZipFileList.class);
-                       res.setContentType("application/zip");
-                       res.setHeader("Content-Disposition", 
"attachment;filename=" + m.fileName); //$NON-NLS-2$
-                       try (OutputStream os = res.getOutputStream()) {
-                               try (ZipOutputStream zos = new 
ZipOutputStream(os)) {
-                                       for (ZipFileEntry e : m)
-                                               e.write(zos);
-                               } catch (Exception e) {
-                                       e.printStackTrace();
-                               }
-                       }
-                       return true;
-               }
-               return false;
-       }
-}
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/jueau/rest/helper/ReaderResourceTest.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/jueau/rest/helper/ReaderResourceTest.java
new file mode 100644
index 0000000..e5433ea
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/jueau/rest/helper/ReaderResourceTest.java
@@ -0,0 +1,109 @@
+// 
***************************************************************************************************************************
+// * 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.jueau.rest.helper;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.helper.*;
+import org.apache.juneau.rest.mock.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+/**
+ * Tests the {@link BasicRestInfoProvider} class.
+ */
+@SuppressWarnings("javadoc")
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ReaderResourceTest {
+
+       @RestResource
+       public static class A {
+
+               @RestMethod
+               public ReaderResource a01() throws Exception {
+                       return ReaderResource.create().contents("foo").build();
+               }
+
+               @RestMethod
+               public ReaderResource a02() throws Exception {
+                       return ReaderResource.create().header("Foo", 
"Bar").build();
+               }
+
+               @RestMethod
+               public ReaderResource a03() throws Exception {
+                       return 
ReaderResource.create().mediaType(MediaType.JSON).build();
+               }
+
+               @RestMethod
+               public ReaderResource a04(RestRequest req) throws Exception {
+                       return 
ReaderResource.create().varResolver(req.getVarResolverSession()).contents("$RQ{foo}").build();
+               }
+
+               @RestMethod
+               public ReaderResource a05() throws Exception {
+                       return ReaderResource.create().contents(new 
ByteArrayInputStream("foo".getBytes())).build();
+               }
+
+               @RestMethod
+               public ReaderResource a06() throws Exception {
+                       return ReaderResource.create().contents(new 
StringReader("foo")).build();
+               }
+
+               @RestMethod
+               public ReaderResource a07() throws Exception {
+                       return ReaderResource.create().contents(new 
StringBuilder("foo")).build();
+               }
+       }
+
+       static MockRest a = MockRest.create(A.class);
+
+       @Test
+       public void a01_basic() throws Exception {
+               assertEquals("foo", a.get("/a01").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a02_headers() throws Exception {
+               assertEquals("Bar", a.get("/a02").execute().getHeader("Foo"));
+       }
+
+       @Test
+       public void a03_contentType() throws Exception {
+               assertEquals("application/json", 
a.get("/a03").execute().getHeader("Content-Type"));
+       }
+
+       @Test
+       public void a04_withVars() throws Exception {
+               assertEquals("bar", 
a.get("/a04?foo=bar").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a05_inputStream() throws Exception {
+               assertEquals("foo", a.get("/a05").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a06_reader() throws Exception {
+               assertEquals("foo", a.get("/a06").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a07_charSequence() throws Exception {
+               assertEquals("foo", a.get("/a07").execute().getBodyAsString());
+       }
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/jueau/rest/helper/StreamResourceTest.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/jueau/rest/helper/StreamResourceTest.java
new file mode 100644
index 0000000..6dc5193
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/jueau/rest/helper/StreamResourceTest.java
@@ -0,0 +1,109 @@
+// 
***************************************************************************************************************************
+// * 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.jueau.rest.helper;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.helper.*;
+import org.apache.juneau.rest.mock.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+/**
+ * Tests the {@link BasicRestInfoProvider} class.
+ */
+@SuppressWarnings("javadoc")
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class StreamResourceTest {
+
+       @RestResource
+       public static class A {
+
+               @RestMethod
+               public StreamResource a01() throws Exception {
+                       return StreamResource.create().contents("foo").build();
+               }
+
+               @RestMethod
+               public StreamResource a02() throws Exception {
+                       return StreamResource.create().header("Foo", 
"Bar").build();
+               }
+
+               @RestMethod
+               public StreamResource a03() throws Exception {
+                       return 
StreamResource.create().mediaType(MediaType.JSON).build();
+               }
+
+               @RestMethod
+               public StreamResource a04() throws Exception {
+                       return 
StreamResource.create().contents("foo".getBytes()).build();
+               }
+
+               @RestMethod
+               public StreamResource a05() throws Exception {
+                       return StreamResource.create().contents(new 
ByteArrayInputStream("foo".getBytes())).build();
+               }
+
+               @RestMethod
+               public StreamResource a06() throws Exception {
+                       return StreamResource.create().contents(new 
StringReader("foo")).build();
+               }
+
+               @RestMethod
+               public StreamResource a07() throws Exception {
+                       return StreamResource.create().contents(new 
StringBuilder("foo")).build();
+               }
+       }
+
+       static MockRest a = MockRest.create(A.class);
+
+       @Test
+       public void a01_basic() throws Exception {
+               assertEquals("foo", a.get("/a01").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a02_headers() throws Exception {
+               assertEquals("Bar", a.get("/a02").execute().getHeader("Foo"));
+       }
+
+       @Test
+       public void a03_contentType() throws Exception {
+               assertEquals("application/json", 
a.get("/a03").execute().getHeader("Content-Type"));
+       }
+
+       @Test
+       public void a04_byteArray() throws Exception {
+               assertEquals("foo", a.get("/a04").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a05_inputStream() throws Exception {
+               assertEquals("foo", a.get("/a05").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a06_reader() throws Exception {
+               assertEquals("foo", a.get("/a06").execute().getBodyAsString());
+       }
+
+       @Test
+       public void a07_charSequence() throws Exception {
+               assertEquals("foo", a.get("/a07").execute().getBodyAsString());
+       }
+}

Reply via email to