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 > juneau-rest-server > 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 > juneau-rest-server > @RestMethod > 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 > juneau-rest-server > 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 > juneau-rest-server > 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 > juneau-rest-server > 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()); + } +}