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 8fa646e  REST refactoring.
8fa646e is described below

commit 8fa646e2c1771e0306393b6b4a77381d34f08ad5
Author: JamesBognar <[email protected]>
AuthorDate: Mon Mar 8 10:07:38 2021 -0500

    REST refactoring.
---
 .../main/ConfigurablePropertyCodeGenerator.java    |  15 +-
 .../java/org/apache/juneau/http/HttpResources.java | 253 +++++++++++
 .../apache/juneau/http/entity/BasicHttpEntity.java | 395 +++++------------
 .../juneau/http/entity/BasicHttpEntity2.java       | 205 ---------
 .../juneau/http/entity/BasicHttpResource.java      | 290 -------------
 .../apache/juneau/http/entity/ByteArrayEntity.java |  12 +-
 .../org/apache/juneau/http/entity/FileEntity.java  |  12 +-
 .../juneau/http/entity/HttpEntityBuilder.java      |   4 +-
 .../juneau/http/entity/InputStreamEntity.java      |  12 +-
 .../apache/juneau/http/entity/ReaderEntity.java    |  12 +-
 .../juneau/http/entity/SerializedEntity.java       |   2 +-
 .../apache/juneau/http/entity/StringEntity.java    |  18 +-
 .../org/apache/juneau/http/header/HeaderList.java  |   8 +-
 .../apache/juneau/http/resource/BasicResource.java | 254 +++++++++++
 .../juneau/http/resource/ByteArrayResource.java    |  37 +-
 .../apache/juneau/http/resource/FileResource.java  |  38 +-
 .../http/{entity => resource}/HttpResource.java    |  24 +-
 .../juneau/http/resource/HttpResourceBuilder.java  | 477 +++++++++++++++++++++
 .../juneau/http/resource/InputStreamResource.java  |  38 +-
 .../juneau/http/resource/ReaderResource.java       |  38 +-
 .../juneau/http/resource/StringResource.java       |  38 +-
 .../apache/juneau/rest/client/ResponseBody.java    |  27 +-
 .../org/apache/juneau/rest/client/RestClient.java  |   1 +
 .../org/apache/juneau/rest/client/RestRequest.java |   8 +-
 .../rest/springboot/BasicSpringRestServlet.java    |   2 +-
 .../org/apache/juneau/rest/BasicRestObject.java    |   2 +-
 .../apache/juneau/rest/BasicRestOperations.java    |   2 +-
 .../org/apache/juneau/rest/BasicRestServlet.java   |   2 +-
 .../org/apache/juneau/rest/BasicStaticFiles.java   |  11 +-
 .../java/org/apache/juneau/rest/StaticFiles.java   |   4 +-
 .../apache/juneau/http/BasicHttpResource_Test.java | 106 ++---
 .../http/remote/Remote_CommonInterfaces_Test.java  |  18 +-
 .../apache/juneau/rest/RestOp_Returns_Test.java    |  34 +-
 .../rest/client/RestClient_BasicCalls_Test.java    |  18 +-
 .../juneau/rest/client/RestClient_Body_Test.java   |  26 +-
 35 files changed, 1403 insertions(+), 1040 deletions(-)

diff --git a/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java 
b/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java
index 40e2be1..2daecca 100644
--- a/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java
+++ b/juneau-all/src/java/main/ConfigurablePropertyCodeGenerator.java
@@ -32,6 +32,7 @@ import org.apache.juneau.http.*;
 import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.part.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.jena.*;
@@ -64,15 +65,13 @@ public class ConfigurablePropertyCodeGenerator {
                ArrayAssertion.class,
                Assertion.class,
                BasicHeader.class,
-               BasicHttpResource.class,
-               HttpResponseBuilder.class,
-               Part.class,
+               BasicHttpResponse.class,
                BasicRuntimeExceptionBuilder.class,
                BasicStatusLineBuilder.class,
                BeanAssertion.class,
                BeanContextBuilder.class,
-               BeanStoreBuilder.class,
                BeanSessionArgs.class,
+               BeanStoreBuilder.class,
                BeanTraverseBuilder.class,
                BooleanAssertion.class,
                ByteArrayAssertion.class,
@@ -125,8 +124,10 @@ public class ConfigurablePropertyCodeGenerator {
                HtmlSchemaSerializerBuilder.class,
                HtmlSerializerBuilder.class,
                HtmlStrippedDocSerializerBuilder.class,
+               HttpEntityBuilder.class,
                HttpExceptionBuilder.class,
-               BasicHttpResponse.class,
+               HttpResourceBuilder.class,
+               HttpResponseBuilder.class,
                IMUsed.class,
                InputStreamParserBuilder.class,
                IntegerAssertion.class,
@@ -159,6 +160,7 @@ public class ConfigurablePropertyCodeGenerator {
                ParserBuilder.class,
                ParserGroupBuilder.class,
                ParserSessionArgs.class,
+               Part.class,
                PartialContent.class,
                PermanentRedirect.class,
                PlainTextParserBuilder.class,
@@ -173,8 +175,9 @@ public class ConfigurablePropertyCodeGenerator {
                RestContextBuilder.class,
                RestOperationContextBuilder.class,
                SeeOther.class,
-               SerializedHeader.class,
                SerializedEntity.class,
+               SerializedEntityBuilder.class,
+               SerializedHeader.class,
                SerializedPart.class,
                SerializerBuilder.class,
                SerializerGroupBuilder.class,
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HttpResources.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HttpResources.java
new file mode 100644
index 0000000..e389970
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/HttpResources.java
@@ -0,0 +1,253 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.http;
+
+import java.io.*;
+import java.util.function.*;
+
+import org.apache.http.*;
+import org.apache.juneau.http.header.*;
+import org.apache.juneau.http.resource.*;
+
+/**
+ * Standard predefined HTTP resources.
+ *
+ * <p>
+ * Resources are simply {@link HttpEntity} objects with arbitrary additional 
headers.
+ */
+public class HttpResources {
+
+       /**
+        * Creates a new {@link ByteArrayResource} builder.
+        *
+        * <p>
+        * Assumes no content type.
+        *
+        * @param content The entity content.  Can be <jk>null<jk>.
+        * @return A new {@link ByteArrayResource} builder.
+        */
+       public static final HttpResourceBuilder<ByteArrayResource> 
byteArrayResource(byte[] content) {
+               return ByteArrayResource.create().content(content);
+       }
+
+       /**
+        * Creates a new {@link ByteArrayResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null<jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @return A new {@link ByteArrayResource} builder.
+        */
+       public static final HttpResourceBuilder<ByteArrayResource> 
byteArrayResource(byte[] content, ContentType contentType) {
+               return 
ByteArrayResource.create().content(content).contentType(contentType);
+       }
+
+       /**
+        * Creates a new {@link ByteArrayResource} builder.
+        *
+        * <p>
+        * Assumes no content type.
+        *
+        * @param content The entity content supplier.  Can be <jk>null<jk>.
+        * @return A new {@link ByteArrayResource} builder.
+        */
+       public static final HttpResourceBuilder<ByteArrayResource> 
byteArrayResource(Supplier<byte[]> content) {
+               return ByteArrayResource.create().contentSupplier(content);
+       }
+
+       /**
+        * Creates a new {@link ByteArrayResource} builder.
+        *
+        * @param content The entity content supplier.  Can be <jk>null<jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @return A new {@link ByteArrayResource} builder.
+        */
+       public static final HttpResourceBuilder<ByteArrayResource> 
byteArrayResource(Supplier<byte[]> content, ContentType contentType) {
+               return 
ByteArrayResource.create().contentSupplier(content).contentType(contentType);
+       }
+
+       /**
+        * Creates a new {@link FileResource} builder.
+        *
+        * <p>
+        * Assumes no content type.
+        *
+        * @param content The entity content.  Can be <jk>null<jk>.
+        * @return A new {@link FileResource} builder.
+        */
+       public static final HttpResourceBuilder<FileResource> fileResource(File 
content) {
+               return FileResource.create().content(content);
+       }
+
+       /**
+        * Creates a new {@link FileResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null<jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @return A new {@link FileResource} builder.
+        */
+       public static final HttpResourceBuilder<FileResource> fileResource(File 
content, ContentType contentType) {
+               return 
FileResource.create().content(content).contentType(contentType);
+       }
+
+       /**
+        * Creates a new {@link ReaderResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null</jk>.
+        * @return A new {@link ReaderResource} builder.
+        */
+       public static final HttpResourceBuilder<ReaderResource> 
readerResource(Reader content) {
+               return ReaderResource.create().content(content);
+       }
+
+       /**
+        * Creates a new {@link ReaderResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null</jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @return A new {@link ReaderResource} builder.
+        */
+       public static final HttpResourceBuilder<ReaderResource> 
readerResource(Reader content, ContentType contentType) {
+               return 
ReaderResource.create().content(content).contentType(contentType);
+       }
+
+//     /**
+//      * Creates a new {@link SerializedResource} object.
+//      *
+//      * @param content
+//      *      The Java POJO representing the content.
+//      *      <br>Can be <jk>null<jk>.
+//      * @param serializer
+//      *      The serializer to use to serialize the POJO.
+//      *      <br>If <jk>null</jk>, POJO will be converted to a string using 
{@link Object#toString()}.
+//      * @return A new {@link SerializedResource} object.
+//      */
+//     public static final SerializedResourceBuilder<SerializedResource> 
serializedResource(Object content, Serializer serializer) {
+//             return 
SerializedResource.create().content(content).serializer(serializer);
+//     }
+//
+//     /**
+//      * Creates a new {@link SerializedResource} object.
+//      *
+//      * @param content
+//      *      The supplier of a Java POJO representing the content.
+//      *      <br>Can be <jk>null<jk>.
+//      * @param serializer
+//      *      The serializer to use to serialize the POJO.
+//      *      <br>If <jk>null</jk>, POJO will be converted to a string using 
{@link Object#toString()}.
+//      * @return A new {@link SerializedResource} object.
+//      */
+//     public static final SerializedResourceBuilder<SerializedResource> 
serializedResource(Supplier<?> content, Serializer serializer) {
+//             return 
SerializedResource.create().contentSupplier(content).serializer(serializer);
+//     }
+//
+//     /**
+//      * Creates a new {@link SerializedResource} object.
+//      *
+//      * @param content
+//      *      The Java POJO representing the content.
+//      *      <br>Can be <jk>null<jk>.
+//      * @param serializer
+//      *      The serializer to use to serialize the POJO.
+//      *      <br>If <jk>null</jk>, POJO will be converted to a string using 
{@link Object#toString()}.
+//      * @param schema
+//      *      Optional HTTP-part schema for providing instructionst to the 
serializer on the format of the entity.
+//      * @return A new {@link SerializedResource} object.
+//      */
+//     public static final SerializedResourceBuilder<SerializedResource> 
serializedResource(Object content, Serializer serializer, HttpPartSchema      
schema) {
+//             return 
SerializedResource.create().content(content).serializer(serializer).schema(schema);
+//     }
+//
+//     /**
+//      * Creates a new {@link SerializedResource} object.
+//      *
+//      * @param content
+//      *      The supplier of a Java POJO representing the content.
+//      *      <br>Can be <jk>null<jk>.
+//      * @param serializer
+//      *      The serializer to use to serialize the POJO.
+//      *      <br>If <jk>null</jk>, POJO will be converted to a string using 
{@link Object#toString()}.
+//      * @param schema
+//      *      Optional HTTP-part schema for providing instructionst to the 
serializer on the format of the entity.
+//      * @return A new {@link SerializedResource} object.
+//      */
+//     public static final SerializedResourceBuilder<SerializedResource> 
serializedResource(Supplier<?> content, Serializer serializer, HttpPartSchema 
schema) {
+//             return 
SerializedResource.create().contentSupplier(content).serializer(serializer).schema(schema);
+//     }
+
+       /**
+        * Creates a new {@link InputStreamResource} builder.
+        *
+        * <p>
+        * Assumes no content type.
+        *
+        * @param content The entity content.  Can be <jk>null<jk>.
+        * @return A new {@link InputStreamResource} builder.
+        */
+       public static final HttpResourceBuilder<InputStreamResource> 
streamResource(InputStream content) {
+               return InputStreamResource.create().content(content);
+       }
+
+       /**
+        * Creates a new {@link InputStreamResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null<jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @param length The content length, or <c>-1</c> if not known.
+        * @return A new {@link InputStreamResource} builder.
+        */
+       public static final HttpResourceBuilder<InputStreamResource> 
streamResource(InputStream content, long length, ContentType contentType) {
+               return 
InputStreamResource.create().content(content).contentLength(length).contentType(contentType);
+       }
+
+       /**
+        * Creates a new builder for a {@link StringResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null</jk>.
+        * @return A new {@link StringResource} builder.
+        */
+       public static final HttpResourceBuilder<StringResource> 
stringResource(String content) {
+               return StringResource.create().content(content);
+       }
+
+       /**
+        * Creates a new builder for a {@link StringResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null</jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @return A new {@link StringResource} builder.
+        */
+       public static final HttpResourceBuilder<StringResource> 
stringResource(String content, ContentType contentType) {
+               return 
StringResource.create().content(content).contentType(contentType);
+       }
+
+       /**
+        * Creates a new builder for a {@link StringResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null</jk>.
+        * @return A new {@link StringResource} builder.
+        */
+       public static final HttpResourceBuilder<StringResource> 
stringResource(Supplier<String> content) {
+               return StringResource.create().contentSupplier(content);
+       }
+
+       /**
+        * Creates a new builder for a {@link StringResource} builder.
+        *
+        * @param content The entity content.  Can be <jk>null</jk>.
+        * @param contentType The entity content type, or <jk>null</jk> if not 
specified.
+        * @return A new {@link StringResource} builder.
+        */
+       public static final HttpResourceBuilder<StringResource> 
stringResource(Supplier<String> content, ContentType contentType) {
+               return 
StringResource.create().contentSupplier(content).contentType(contentType);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity.java
index decac35..b118f53 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity.java
@@ -12,7 +12,10 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.http.entity;
 
+import static org.apache.juneau.internal.IOUtils.*;
+
 import java.io.*;
+import java.nio.charset.*;
 import java.util.function.*;
 
 import org.apache.http.*;
@@ -22,393 +25,193 @@ import org.apache.juneau.http.header.*;
 import org.apache.juneau.internal.*;
 
 /**
- * An extension of {@link org.apache.http.entity.BasicHttpEntity} with 
additional features.
+ * A basic {@link org.apache.http.HttpEntity} implementation with additional 
features.
  *
  * Provides the following features:
  * <ul class='spaced-list'>
  *     <li>
- *             Default support for various streams and readers.
- *     <li>
- *             Content from {@link Supplier Suppliers}.
- *     <li>
  *             Caching.
  *     <li>
  *             Fluent setters.
  *     <li>
  *             Fluent assertions.
+ *     <li>
+ *             Externally-supplied/dynamic content.
  * </ul>
  */
-@FluentSetters
 @BeanIgnore
-@Deprecated
-public class BasicHttpEntity extends org.apache.http.entity.BasicHttpEntity {
-       private Object content;
-       private boolean cache;
+public class BasicHttpEntity implements HttpEntity {
 
        /**
-        * Creator.
-        *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        * @return A new empty {@link BasicHttpEntity} object.
+        * An empty HttpEntity.
         */
-       public static BasicHttpEntity of(Object content) {
-               return new BasicHttpEntity(content);
-       }
+       public static final BasicHttpEntity EMPTY = 
create(BasicHttpEntity.class).build();
 
-       /**
-        * Creator.
-        *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        * @return A new empty {@link BasicHttpEntity} object.
-        */
-       public static BasicHttpEntity of(Supplier<?> content) {
-               return new BasicHttpEntity(content);
-       }
+       final boolean cached, chunked;
+       final Object content;
+       final Supplier<?> contentSupplier;
+       final ContentType contentType;
+       final ContentEncoding contentEncoding;
+       final Charset charset;
+       final long length;
 
        /**
-        * Creates a new basic entity.
+        * Creates a builder for this class.
         *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
+        * @param implClass The subclass that the builder is going to create.
+        * @return A new builder bean.
         */
-       public BasicHttpEntity(Object content) {
-               this(content, null, null);
+       public static <T extends BasicHttpEntity> HttpEntityBuilder<T> 
create(Class<T> implClass) {
+               return new HttpEntityBuilder<>(implClass);
        }
 
        /**
         * Constructor.
         *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        * @param contentType
-        *      The content type of the contents.
-        *      <br>Can be <jk>null</jk>.
-        * @param contentEncoding
-        *      The content encoding of the contents.
-        *      <br>Can be <jk>null</jk>.
+        * @param builder The builder containing the arguments for this bean.
         */
-       public BasicHttpEntity(Object content, ContentType contentType, 
ContentEncoding contentEncoding) {
-               super();
-               this.content = content;
-               contentType(contentType);
-               contentEncoding(contentEncoding);
+       public BasicHttpEntity(HttpEntityBuilder<?> builder) {
+               this.cached = builder.cached;
+               this.chunked = builder.chunked;
+               this.content = builder.content;
+               this.contentSupplier = builder.contentSupplier;
+               this.contentType = builder.contentType;
+               this.contentEncoding = builder.contentEncoding;
+               this.charset = builder.charset;
+               this.length = builder.contentLength;
        }
 
        /**
-        * Shortcut for calling {@link #setContentType(String)}.
+        * Creates a builder for this class initialized with the contents of 
this bean.
         *
-        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpEntity contentType(String value) {
-               super.setContentType(value);
-               return this;
-       }
-
-       /**
-        * Shortcut for calling {@link #setContentType(Header)}.
-        *
-        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpEntity contentType(Header value) {
-               super.setContentType(value);
-               return this;
-       }
-
-       /**
-        * Shortcut for calling {@link #setContentLength(long)}.
+        * <p>
+        * Allows you to create a modifiable copy of this bean.
         *
-        * @param value The new <c>Content-Length</c> header value.
-        * @return This object (for method chaining).
+        * @return A new builder bean.
         */
-       @FluentSetter
-       public BasicHttpEntity contentLength(long value) {
-               super.setContentLength(value);
-               return this;
+       public HttpEntityBuilder<? extends BasicHttpEntity> copy() {
+               return new HttpEntityBuilder<>(this);
        }
 
        /**
-        * Shortcut for calling {@link #setContentEncoding(String)}.
+        * Converts the contents of this entity as a string.
         *
-        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpEntity contentEncoding(String value) {
-               super.setContentEncoding(value);
-               return this;
-       }
-
-       /**
-        * Shortcut for calling {@link #setContentEncoding(Header)}.
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
         *
-        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
-        * @return This object (for method chaining).
+        * @return The contents of this entity as a string.
+        * @throws IOException If a problem occurred while trying to read the 
content.
         */
-       @FluentSetter
-       public BasicHttpEntity contentEncoding(Header value) {
-               super.setContentEncoding(value);
-               return this;
+       public String asString() throws IOException {
+               return read(getContent());
        }
 
        /**
-        * Shortcut for calling {@link #setChunked(boolean)} with <jk>true</jk>.
+        * Converts the contents of this entity as a byte array.
         *
-        * <ul class='notes'>
-        *      <li>If the {@link #getContentLength()} method returns a 
negative value, the HttpClient code will always
-        *              use chunked encoding.
-        * </ul>
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
         *
-        * @return This object (for method chaining).
+        * @return The contents of this entity as a byte array.
+        * @throws IOException If a problem occurred while trying to read the 
content.
         */
-       @FluentSetter
-       public BasicHttpEntity chunked() {
-               return chunked(true);
+       public byte[] asBytes() throws IOException {
+               return readBytes(getContent());
        }
 
        /**
-        * Shortcut for calling {@link #setChunked(boolean)}.
-        *
-        * <ul class='notes'>
-        *      <li>If the {@link #getContentLength()} method returns a 
negative value, the HttpClient code will always
-        *              use chunked encoding.
-        * </ul>
+        * Returns an assertion on the contents of this entity.
         *
-        * @param value The new value for this flag.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpEntity chunked(boolean value) {
-               super.setChunked(value);
-               return this;
-       }
-
-       /**
-        * Specifies that the contents of this resource should be cached into 
an internal byte array so that it can
-        * be read multiple times.
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
         *
-        * @return This object (for method chaining).
+        * @return A new fluent assertion.
+        * @throws IOException If a problem occurred while trying to read the 
byte array.
         */
-       @FluentSetter
-       public BasicHttpEntity cache() {
-               return cache(true);
+       public FluentStringAssertion<BasicHttpEntity> assertString() throws 
IOException {
+               return new FluentStringAssertion<>(asString(), this);
        }
 
        /**
-        * Specifies that the contents of this resource should be cached into 
an internal byte array so that it can
-        * be read multiple times.
+        * Returns an assertion on the contents of this entity.
         *
-        * @param value The new value for this flag.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpEntity cache(boolean value) {
-               this.cache = value;
-               return this;
-       }
-
-       /**
-        * Converts the contents of this entity as a byte array.
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
         *
-        * @return The contents of this entity as a byte array.
+        * @return A new fluent assertion.
         * @throws IOException If a problem occurred while trying to read the 
byte array.
         */
-       public String asString() throws IOException {
-               return IOUtils.read(getContent());
+       public FluentByteArrayAssertion<BasicHttpEntity> assertBytes() throws 
IOException {
+               return new FluentByteArrayAssertion<>(asBytes(), this);
        }
 
        /**
-        * Converts the contents of this entity as a byte array.
+        * Returns the content of this entity.
         *
-        * @return The contents of this entity as a byte array.
-        * @throws IOException If a problem occurred while trying to read the 
byte array.
+        * @param def The default value if <jk>null</jk>.
+        * @return The content object.
         */
-       public byte[] asBytes() throws IOException {
-               try (InputStream o = getContent()) {
-                       return o == null ? null : IOUtils.readBytes(o);
-               }
+       @SuppressWarnings("unchecked")
+       protected <T> T contentOrElse(T def) {
+               Object o = content;
+               if (o == null && contentSupplier != null)
+                       o = contentSupplier.get();
+               return (o == null ? def : (T)o);
        }
 
        /**
-        * Returns an assertion on the contents of this entity.
+        * Returns <jk>true</jk> if the contents of this entity are provided 
through an external supplier.
         *
-        * @return A new fluent assertion.
-        * @throws IOException If a problem occurred while trying to read the 
byte array.
+        * <p>
+        * Externally supplied content generally means the content length 
cannot be reliably determined
+        * based on the content.
+        *
+        * @return <jk>true</jk> if the contents of this entity are provided 
through an external supplier.
         */
-       public FluentStringAssertion<BasicHttpEntity> assertString() throws 
IOException {
-               return new FluentStringAssertion<>(asString(), this);
+       protected boolean isSupplied() {
+               return contentSupplier != null;
        }
 
-       /**
-        * Returns an assertion on the contents of this entity.
-        *
-        * @return A new fluent assertion.
-        * @throws IOException If a problem occurred while trying to read the 
byte array.
-        */
-       public FluentByteArrayAssertion<BasicHttpEntity> assertBytes() throws 
IOException {
-               return new FluentByteArrayAssertion<>(asBytes(), this);
+       @Override /* HttpEntity */
+       public long getContentLength() {
+               return length;
        }
 
        @Override
        public boolean isRepeatable() {
-               Object o = getRawContent();
-               return cache || o instanceof File || o instanceof CharSequence 
|| o instanceof byte[];
+               return false;
        }
 
        @Override
-       public long getContentLength() {
-               long x = super.getContentLength();
-               if (x != -1)
-                       return x;
-               try {
-                       tryCache();
-               } catch (IOException e) {}
-               Object o = getRawContent();
-               if (o instanceof byte[])
-                       return ((byte[])o).length;
-               if (o instanceof File)
-                       return ((File)o).length();
-               if (o instanceof CharSequence)
-                       return ((CharSequence)o).length();
-               return -1;
+       public boolean isChunked() {
+               return chunked;
        }
 
        @Override
-       public InputStream getContent() {
-               try {
-                       tryCache();
-                       Object o = getRawContent();
-                       if (o == null)
-                               return null;
-                       if (o instanceof File)
-                               return new FileInputStream((File)o);
-                       if (o instanceof byte[])
-                               return new ByteArrayInputStream((byte[])o);
-                       if (o instanceof Reader)
-                               return new ReaderInputStream((Reader)o, 
IOUtils.UTF8);
-                       if (o instanceof InputStream)
-                               return (InputStream)o;
-                       return new ReaderInputStream(new 
StringReader(o.toString()),IOUtils.UTF8);
-               } catch (Exception e) {
-                       throw new RuntimeException(e);
-               }
+       public Header getContentType() {
+               return contentType;
        }
 
        @Override
-       public void writeTo(OutputStream os) throws IOException {
-               tryCache();
-               Object o = getRawContent();
-               if (o != null) {
-                       IOUtils.pipe(o, os);
-               }
-               os.flush();
+       public Header getContentEncoding() {
+               return contentEncoding;
        }
 
        @Override
        public boolean isStreaming() {
-               Object o = getRawContent();
-               return (o instanceof InputStream || o instanceof Reader);
-       }
-
-       /**
-        * Returns the raw content of this resource.
-        *
-        * @return The raw content of this resource.
-        */
-       protected Object getRawContent() {
-               return unwrap(content);
+               return false;
        }
 
-       private void tryCache() throws IOException {
-               Object o = getRawContent();
-               if (cache && isCacheable(o))
-                       this.content = readBytes(o);
-       }
-
-       /**
-        * Returns <jk>true</jk> if the specified object is cachable as a byte 
array.
-        *
-        * <p>
-        * The default implementation returns <jk>true</jk> for the following 
types:
-        * <ul>
-        *      <li>{@link File}
-        *      <li>{@link InputStream}
-        *      <li>{@link Reader}
-        *
-        * @param o The object to check.
-        * @return <jk>true</jk> if the specified object is cachable as a byte 
array.
-        */
-       protected boolean isCacheable(Object o) {
-               return (o instanceof File || o instanceof InputStream || o 
instanceof Reader);
-       }
+       @Override
+       public void consumeContent() throws IOException {}
 
-       /**
-        * Reads the contents of the specified object as a byte array.
-        *
-        * @param o The object to read.
-        * @return The byte array contents.
-        * @throws IOException If object could not be read.
-        */
-       protected byte[] readBytes(Object o) throws IOException {
-               return IOUtils.readBytes(o);
+       @Override
+       public InputStream getContent() throws IOException, 
UnsupportedOperationException {
+               return IOUtils.EMPTY_INPUT_STREAM;
        }
 
-       /**
-        * If the specified object is a {@link Supplier}, returns the supplied 
value, otherwise the same value.
-        *
-        * @param o The object to unwrap.
-        * @return The unwrapped object.
-        */
-       protected Object unwrap(Object o) {
-               while (o instanceof Supplier)
-                       o = ((Supplier<?>)o).get();
-               return o;
-       }
+       @Override
+       public void writeTo(OutputStream outStream) throws IOException {}
 
        // <FluentSetters>
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity2.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity2.java
deleted file mode 100644
index 8f7017c..0000000
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpEntity2.java
+++ /dev/null
@@ -1,205 +0,0 @@
-// 
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                
                                              *
-// *                                                                           
                                              *
-// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
-// *                                                                           
                                              *
-// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the 
License.                                              *
-// 
***************************************************************************************************************************
-package org.apache.juneau.http.entity;
-
-import static org.apache.juneau.internal.IOUtils.*;
-
-import java.io.*;
-import java.nio.charset.*;
-import java.util.function.*;
-
-import org.apache.http.*;
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.assertions.*;
-import org.apache.juneau.http.header.*;
-
-/**
- * A basic {@link org.apache.http.HttpEntity} implementation with additional 
features.
- *
- * Provides the following features:
- * <ul class='spaced-list'>
- *     <li>
- *             Caching.
- *     <li>
- *             Fluent setters.
- *     <li>
- *             Fluent assertions.
- *     <li>
- *             Externally-supplied/dynamic content.
- * </ul>
- */
-@BeanIgnore
-public abstract class BasicHttpEntity2 implements HttpEntity {
-
-       final boolean cached, chunked;
-       final Object content;
-       final Supplier<?> contentSupplier;
-       final ContentType contentType;
-       final ContentEncoding contentEncoding;
-       final Charset charset;
-       final long length;
-
-       /**
-        * Creates a builder for this class.
-        *
-        * @param implClass The subclass that the builder is going to create.
-        * @return A new builder bean.
-        */
-       public static <T extends BasicHttpEntity2> HttpEntityBuilder<T> 
create(Class<T> implClass) {
-               return new HttpEntityBuilder<>(implClass);
-       }
-
-       /**
-        * Constructor.
-        *
-        * @param builder The builder containing the arguments for this bean.
-        */
-       public BasicHttpEntity2(HttpEntityBuilder<?> builder) {
-               this.cached = builder.cached;
-               this.chunked = builder.chunked;
-               this.content = builder.content;
-               this.contentSupplier = builder.contentSupplier;
-               this.contentType = builder.contentType;
-               this.contentEncoding = builder.contentEncoding;
-               this.charset = builder.charset;
-               this.length = builder.contentLength;
-       }
-
-       /**
-        * Creates a builder for this class initialized with the contents of 
this bean.
-        *
-        * <p>
-        * Allows you to create a modifiable copy of this bean.
-        *
-        * @return A new builder bean.
-        */
-       public HttpEntityBuilder<? extends BasicHttpEntity2> builder() {
-               return new HttpEntityBuilder<>(this);
-       }
-
-       /**
-        * Converts the contents of this entity as a string.
-        *
-        * <p>
-        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
-        *
-        * @return The contents of this entity as a string.
-        * @throws IOException If a problem occurred while trying to read the 
content.
-        */
-       public String asString() throws IOException {
-               return read(getContent());
-       }
-
-       /**
-        * Converts the contents of this entity as a byte array.
-        *
-        * <p>
-        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
-        *
-        * @return The contents of this entity as a byte array.
-        * @throws IOException If a problem occurred while trying to read the 
content.
-        */
-       public byte[] asBytes() throws IOException {
-               return readBytes(getContent());
-       }
-
-       /**
-        * Returns an assertion on the contents of this entity.
-        *
-        * <p>
-        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
-        *
-        * @return A new fluent assertion.
-        * @throws IOException If a problem occurred while trying to read the 
byte array.
-        */
-       public FluentStringAssertion<BasicHttpEntity2> assertString() throws 
IOException {
-               return new FluentStringAssertion<>(asString(), this);
-       }
-
-       /**
-        * Returns an assertion on the contents of this entity.
-        *
-        * <p>
-        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
-        *
-        * @return A new fluent assertion.
-        * @throws IOException If a problem occurred while trying to read the 
byte array.
-        */
-       public FluentByteArrayAssertion<BasicHttpEntity2> assertBytes() throws 
IOException {
-               return new FluentByteArrayAssertion<>(asBytes(), this);
-       }
-
-       /**
-        * Returns the content of this entity.
-        *
-        * @param def The default value if <jk>null</jk>.
-        * @return The content object.
-        */
-       @SuppressWarnings("unchecked")
-       protected <T> T contentOrElse(T def) {
-               Object o = content;
-               if (o == null && contentSupplier != null)
-                       o = contentSupplier.get();
-               return (o == null ? def : (T)o);
-       }
-
-       /**
-        * Returns <jk>true</jk> if the contents of this entity are provided 
through an external supplier.
-        * 
-        * <p>
-        * Externally supplied content generally means the content length 
cannot be reliably determined
-        * based on the content.
-        *
-        * @return <jk>true</jk> if the contents of this entity are provided 
through an external supplier.
-        */
-       protected boolean isSupplied() {
-               return contentSupplier != null;
-       }
-
-       @Override /* HttpEntity */
-       public long getContentLength() {
-               return length;
-       }
-
-       @Override
-       public boolean isRepeatable() {
-               return false;
-       }
-
-       @Override
-       public boolean isChunked() {
-               return chunked;
-       }
-
-       @Override
-       public Header getContentType() {
-               return contentType;
-       }
-
-       @Override
-       public Header getContentEncoding() {
-               return contentEncoding;
-       }
-
-       @Override
-       public boolean isStreaming() {
-               return false;
-       }
-
-       @Override
-       public void consumeContent() throws IOException {}
-
-       // <FluentSetters>
-
-       // </FluentSetters>
-}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpResource.java
deleted file mode 100644
index 2f41527..0000000
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/BasicHttpResource.java
+++ /dev/null
@@ -1,290 +0,0 @@
-// 
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                
                                              *
-// *                                                                           
                                              *
-// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
-// *                                                                           
                                              *
-// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the 
License.                                              *
-// 
***************************************************************************************************************************
-package org.apache.juneau.http.entity;
-
-import static org.apache.juneau.internal.ObjectUtils.*;
-
-import java.util.*;
-import java.util.function.*;
-
-import org.apache.http.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.internal.*;
-import org.apache.juneau.http.header.*;
-
-/**
- * An extension of an {@link HttpEntity} with support for arbitrary headers.
- *
- * Provides the following features:
- * <ul class='spaced-list'>
- *     <li>
- *             Default support for various streams and readers.
- *     <li>
- *             Content from {@link Supplier Suppliers}.
- *     <li>
- *             Caching.
- *     <li>
- *             Fluent setters.
- *     <li>
- *             Fluent assertions.
- * </ul>
- */
-@SuppressWarnings("deprecation")
-@FluentSetters
-public class BasicHttpResource extends BasicHttpEntity implements HttpResource 
{
-
-       private final List<Header> headers = AList.create();
-
-       /**
-        * Creator.
-        *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        * @return A new empty {@link BasicHttpResource} object.
-        */
-       public static BasicHttpResource of(Object content) {
-               return new BasicHttpResource(content);
-       }
-
-       /**
-        * Creator.
-        *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        * @return A new empty {@link BasicHttpResource} object.
-        */
-       public static BasicHttpResource of(Supplier<?> content) {
-               return new BasicHttpResource(content);
-       }
-
-       /**
-        * Constructor.
-        *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        */
-       public BasicHttpResource(Object content) {
-               super(content);
-       }
-
-       /**
-        * Constructor.
-        *
-        * @param content
-        *      The content.
-        *      <br>Can be any of the following:
-        *      <ul>
-        *              <li><c>InputStream</c>
-        *              <li><c>Reader</c> - Converted to UTF-8 bytes.
-        *              <li><c>File</c>
-        *              <li><c>CharSequence</c> - Converted to UTF-8 bytes.
-        *              <li><c><jk>byte</jk>[]</c>.
-        *              <li>A {@link Supplier} of anything on this list.
-        *      </ul>
-        * </ul>
-        * @param contentType
-        *      The content type of the contents.
-        *      <br>Can be <jk>null</jk>.
-        * @param contentEncoding
-        *      The content encoding of the contents.
-        *      <br>Can be <jk>null</jk>.
-        */
-       public BasicHttpResource(Object content, ContentType contentType, 
ContentEncoding contentEncoding) {
-               super(content, contentType, contentEncoding);
-       }
-
-       /**
-        * Adds an arbitrary header to this resource.
-        *
-        * <p>
-        * Header is not added if name or value is null.
-        *
-        * @param name The header name.
-        * @param val The header value.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpResource header(String name, Object val) {
-               if (name != null && val != null)
-                       headers.add(BasicHeader.of(name, val));
-               return this;
-       }
-
-       /**
-        * Adds an arbitrary header to this resource.
-        *
-        * @param value The header.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpResource header(Header value) {
-               headers.add(value);
-               return this;
-       }
-
-       /**
-        * Adds an arbitrary collection of headers to this resource.
-        *
-        * @param headers The headers to add to this resource.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpResource headers(List<Header> headers) {
-               this.headers.addAll(headers);
-               return this;
-       }
-
-       /**
-        * Adds an arbitrary collection of headers to this resource.
-        *
-        * @param headers The headers to add to this resource.
-        * @return This object (for method chaining).
-        */
-       @FluentSetter
-       public BasicHttpResource headers(Header...headers) {
-               this.headers.addAll(Arrays.asList(headers));
-               return this;
-       }
-
-       /**
-        * Returns the first header with the specified name as a string.
-        *
-        * @param name The header name.
-        * @return The header value or <jk>null</jk> if header was not found.
-        */
-       public String getStringHeader(String name) {
-               Header h = getLastHeader(name);
-               return h == null ? null : h.getValue();
-       }
-
-       /**
-        * Returns the first header with the specified name as a string.
-        *
-        * @param name The header name.
-        * @return The header or <jk>null</jk> if header was not found.
-        */
-       public Header getFirstHeader(String name) {
-               for (Header h : headers)
-                       if (h != null && eq(name, h.getName()))
-                               return h;
-               return null;
-       }
-
-       /**
-        * Returns the last header with the specified name as a string.
-        *
-        * @param name The header name.
-        * @return The header or <jk>null</jk> if header was not found.
-        */
-       public Header getLastHeader(String name) {
-               for (ListIterator<Header> li = 
headers.listIterator(headers.size()); li.hasPrevious();) {
-                       Header h = li.previous();
-                       if (h != null && eq(name, h.getName()))
-                               return h;
-               }
-               return null;
-       }
-
-       @Override /* Resource */
-       public List<Header> getHeaders() {
-               return Collections.unmodifiableList(headers);
-       }
-
-       // <FluentSetters>
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource cache() {
-               super.cache();
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource cache(boolean value) {
-               super.cache(value);
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource chunked() {
-               super.chunked();
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource chunked(boolean value) {
-               super.chunked(value);
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource contentEncoding(String value) {
-               super.contentEncoding(value);
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource contentEncoding(Header value) {
-               super.contentEncoding(value);
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource contentLength(long value) {
-               super.contentLength(value);
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource contentType(String value) {
-               super.contentType(value);
-               return this;
-       }
-
-       @Override /* GENERATED - BasicHttpEntity */
-       public BasicHttpResource contentType(Header value) {
-               super.contentType(value);
-               return this;
-       }
-
-       // </FluentSetters>
-}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ByteArrayEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ByteArrayEntity.java
index 58883ed..bf09ca1 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ByteArrayEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ByteArrayEntity.java
@@ -20,7 +20,7 @@ import java.io.*;
 /**
  * A repeatable entity that obtains its content from a byte array.
  */
-public class ByteArrayEntity extends BasicHttpEntity2 {
+public class ByteArrayEntity extends BasicHttpEntity {
 
        private static final byte[] EMPTY = new byte[0];
 
@@ -42,6 +42,16 @@ public class ByteArrayEntity extends BasicHttpEntity2 {
                super(builder);
        }
 
+       /**
+        * Creates a new {@link ByteArrayEntity} builder initialized with the 
contents of this entity.
+        *
+        * @return A new {@link ByteArrayEntity} builder initialized with the 
contents of this entity.
+        */
+       @Override /* BasicHttpEntity */
+       public HttpEntityBuilder<ByteArrayEntity> copy() {
+               return new HttpEntityBuilder<>(this);
+       }
+
        private byte[] bytes() {
                return contentOrElse(EMPTY);
        }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/FileEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/FileEntity.java
index 9809336..a242cd7 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/FileEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/FileEntity.java
@@ -22,7 +22,7 @@ import org.apache.juneau.internal.*;
 /**
  * A repeatable entity that obtains its content from a {@link File}.
  */
-public class FileEntity extends BasicHttpEntity2 {
+public class FileEntity extends BasicHttpEntity {
 
        private final File content;
        private final byte[] cache;
@@ -48,6 +48,16 @@ public class FileEntity extends BasicHttpEntity2 {
                cache = builder.cached ? readBytes(content) : null;
        }
 
+       /**
+        * Creates a new {@link FileEntity} builder initialized with the 
contents of this entity.
+        *
+        * @return A new {@link FileEntity} builder initialized with the 
contents of this entity.
+        */
+       @Override /* BasicHttpEntity */
+       public HttpEntityBuilder<FileEntity> copy() {
+               return new HttpEntityBuilder<>(this);
+       }
+
        @Override /* AbstractHttpEntity */
        public String asString() throws IOException {
                return read(content);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpEntityBuilder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpEntityBuilder.java
index 09494e7..7ba7c88 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpEntityBuilder.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpEntityBuilder.java
@@ -26,7 +26,7 @@ import org.apache.juneau.internal.*;
  * @param <T> The bean type to create for this builder.
  */
 @FluentSetters(returns="HttpEntityBuilder<T>")
-public class HttpEntityBuilder<T extends BasicHttpEntity2> {
+public class HttpEntityBuilder<T extends BasicHttpEntity> {
 
        boolean cached, chunked;
        Object content;
@@ -37,7 +37,7 @@ public class HttpEntityBuilder<T extends BasicHttpEntity2> {
        long contentLength = -1;
 
        /** The HttpEntity implementation class. */
-       protected final Class<? extends BasicHttpEntity2> implClass;
+       protected final Class<? extends BasicHttpEntity> implClass;
 
        /**
         * Constructor.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/InputStreamEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/InputStreamEntity.java
index 80c28d6..43f62c3 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/InputStreamEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/InputStreamEntity.java
@@ -20,7 +20,7 @@ import java.io.*;
 /**
  * A streamed, non-repeatable entity that obtains its content from an {@link 
InputStream}.
  */
-public class InputStreamEntity extends BasicHttpEntity2 {
+public class InputStreamEntity extends BasicHttpEntity {
 
        private final InputStream content;
        private final long maxLength;
@@ -48,6 +48,16 @@ public class InputStreamEntity extends BasicHttpEntity2 {
                maxLength = builder.contentLength == -1 && cache != null ? 
cache.length : builder.contentLength;
        }
 
+       /**
+        * Creates a new {@link InputStreamEntity} builder initialized with the 
contents of this entity.
+        *
+        * @return A new {@link InputStreamEntity} builder initialized with the 
contents of this entity.
+        */
+       @Override /* BasicHttpEntity */
+       public HttpEntityBuilder<InputStreamEntity> copy() {
+               return new HttpEntityBuilder<>(this);
+       }
+
        @Override /* AbstractHttpEntity */
        public String asString() throws IOException {
                return new String(asBytes(), UTF8);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ReaderEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ReaderEntity.java
index ca733b1..2cd1dd2 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ReaderEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/ReaderEntity.java
@@ -24,7 +24,7 @@ import org.apache.juneau.internal.*;
 /**
  * A streamed, non-repeatable entity that obtains its content from an {@link 
Reader}.
  */
-public class ReaderEntity extends BasicHttpEntity2 {
+public class ReaderEntity extends BasicHttpEntity {
 
        private final Reader content;
        private final long contentLength;
@@ -54,6 +54,16 @@ public class ReaderEntity extends BasicHttpEntity2 {
                contentLength = builder.contentLength == -1 && cache != null ? 
cache.length : builder.contentLength;
        }
 
+       /**
+        * Creates a new {@link ReaderEntity} builder initialized with the 
contents of this entity.
+        *
+        * @return A new {@link ReaderEntity} builder initialized with the 
contents of this entity.
+        */
+       @Override /* BasicHttpEntity */
+       public HttpEntityBuilder<ReaderEntity> copy() {
+               return new HttpEntityBuilder<>(this);
+       }
+
        @Override /* AbstractHttpEntity */
        public String asString() throws IOException {
                return cache == null ? read(content) : new String(cache, UTF8);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/SerializedEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/SerializedEntity.java
index 1650cc9..ef54591 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/SerializedEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/SerializedEntity.java
@@ -23,7 +23,7 @@ import org.apache.juneau.serializer.*;
 /**
  * HttpEntity for serializing POJOs as the body of HTTP requests.
  */
-public class SerializedEntity extends BasicHttpEntity2 {
+public class SerializedEntity extends BasicHttpEntity {
        final Serializer serializer;
        HttpPartSchema schema;
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/StringEntity.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/StringEntity.java
index a199507..416c563 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/StringEntity.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/StringEntity.java
@@ -24,7 +24,7 @@ import org.apache.juneau.internal.*;
 /**
  * A self contained, repeatable entity that obtains its content from a {@link 
String}.
  */
-public class StringEntity extends BasicHttpEntity2 {
+public class StringEntity extends BasicHttpEntity {
 
        private static final String EMPTY = "";
 
@@ -51,8 +51,13 @@ public class StringEntity extends BasicHttpEntity2 {
                cache = builder.cached ? string().getBytes(charset) : null;
        }
 
-       @Override
-       public HttpEntityBuilder<StringEntity> builder() {
+       /**
+        * Creates a new {@link StringEntity} builder initialized with the 
contents of this entity.
+        *
+        * @return A new {@link StringEntity} builder initialized with the 
contents of this entity.
+        */
+       @Override /* BasicHttpEntity */
+       public HttpEntityBuilder<StringEntity> copy() {
                return new HttpEntityBuilder<>(this);
        }
 
@@ -87,7 +92,12 @@ public class StringEntity extends BasicHttpEntity2 {
 
        @Override /* HttpEntity */
        public InputStream getContent() throws IOException {
-               return cache == null ? new ReaderInputStream(new 
StringReader(string()), charset) : new ByteArrayInputStream(cache);
+               if (cache != null)
+                       return new ByteArrayInputStream(cache);
+               String s = string();
+               if (s == null)
+                       return IOUtils.EMPTY_INPUT_STREAM;
+               return new ReaderInputStream(new StringReader(s), charset);
        }
 
        @Override /* HttpEntity */
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/HeaderList.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/HeaderList.java
index c05d086..00baa8f 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/HeaderList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/HeaderList.java
@@ -181,7 +181,7 @@ public class HeaderList implements Iterable<Header> {
                List<Header> l = null;
                for (int i = 0; i < headers.size(); i++) {  // See HTTPCORE-361
                        Header x = headers.get(i);
-                       if (x.getName().equalsIgnoreCase(name)) {
+                       if (isEqualsIc(x.getName(), name)) {
                                if (l == null)
                                        l = new ArrayList<>();
                                l.add(x);
@@ -202,7 +202,7 @@ public class HeaderList implements Iterable<Header> {
        public Header getFirst(String name) {
                for (int i = 0; i < headers.size(); i++) {  // See HTTPCORE-361
                        Header x = headers.get(i);
-                       if (x.getName().equalsIgnoreCase(name))
+                       if (isEqualsIc(x.getName(), name))
                                return x;
                }
                return null;
@@ -220,7 +220,7 @@ public class HeaderList implements Iterable<Header> {
        public Header getLast(String name) {
                for (int i = headers.size() - 1; i >= 0; i--) {
                        Header x = headers.get(i);
-                       if (x.getName().equalsIgnoreCase(name))
+                       if (isEqualsIc(x.getName(), name))
                                return x;
                }
                return null;
@@ -247,7 +247,7 @@ public class HeaderList implements Iterable<Header> {
        public boolean contains(String name) {
                for (int i = 0; i < headers.size(); i++) {  // See HTTPCORE-361
                        Header x = headers.get(i);
-                       if (x.getName().equalsIgnoreCase(name))
+                       if (isEqualsIc(x.getName(), name))
                                return true;
                }
                return false;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/BasicResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/BasicResource.java
new file mode 100644
index 0000000..53d868e
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/BasicResource.java
@@ -0,0 +1,254 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.http.resource;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.http.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.assertions.*;
+import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.header.*;
+
+/**
+ * A basic {@link org.apache.juneau.http.resource.HttpResource} implementation 
with additional features.
+ *
+ * Provides the following features:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             Caching.
+ *     <li>
+ *             Fluent setters.
+ *     <li>
+ *             Fluent assertions.
+ *     <li>
+ *             Externally-supplied/dynamic content.
+ * </ul>
+ */
+@BeanIgnore
+public class BasicResource implements HttpResource {
+
+       final BasicHttpEntity entity;
+       final HeaderList headers;
+
+       /**
+        * Creates a builder for this class.
+        *
+        * @param implClass The subclass that the builder is going to create.
+        * @param entityImplClass The entity subclass that the builder is going 
to create.
+        * @return A new builder bean.
+        */
+       public static <T extends BasicResource> HttpResourceBuilder<T> 
create(Class<T> implClass, Class<? extends BasicHttpEntity> entityImplClass) {
+               return new HttpResourceBuilder<>(implClass, entityImplClass);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param builder The builder containing the arguments for this bean.
+        */
+       public BasicResource(HttpResourceBuilder<?> builder) {
+               this.entity = builder.entity();
+               this.headers = builder.headers();
+       }
+
+       /**
+        * Constructor.
+        *
+        * <p>
+        * This is the constructor used when parsing an HTTP response.
+        *
+        * @param response The HTTP response to copy from.  Must not be 
<jk>null</jk>.
+        * @throws IOException Rethrown from {@link HttpEntity#getContent()}.
+        */
+       public BasicResource(HttpResponse response) throws IOException {
+               this(create(null, InputStreamEntity.class).copyFrom(response));
+       }
+
+       /**
+        * Creates a builder for this class initialized with the contents of 
this bean.
+        *
+        * <p>
+        * Allows you to create a modifiable copy of this bean.
+        *
+        * @return A new builder bean.
+        */
+       public HttpResourceBuilder<? extends BasicResource> copy() {
+               return new HttpResourceBuilder<>(this);
+       }
+
+       /**
+        * Converts the contents of the entity of this resource as a string.
+        *
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
+        *
+        * @return The contents of this entity as a string.
+        * @throws IOException If a problem occurred while trying to read the 
content.
+        */
+       public String asString() throws IOException {
+               return entity.asString();
+       }
+
+       /**
+        * Converts the contents of the entity of this resource as a byte array.
+        *
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
+        *
+        * @return The contents of this entity as a byte array.
+        * @throws IOException If a problem occurred while trying to read the 
content.
+        */
+       public byte[] asBytes() throws IOException {
+               return entity.asBytes();
+       }
+
+       /**
+        * Returns an assertion on the contents of the entity of this resource.
+        *
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
+        *
+        * @return A new fluent assertion.
+        * @throws IOException If a problem occurred while trying to read the 
byte array.
+        */
+       public FluentStringAssertion<BasicResource> assertString() throws 
IOException {
+               return new FluentStringAssertion<>(asString(), this);
+       }
+
+       /**
+        * Returns an assertion on the contents of the entity of this resource.
+        *
+        * <p>
+        * Note that this may exhaust the content on non-repeatable, non-cached 
entities.
+        *
+        * @return A new fluent assertion.
+        * @throws IOException If a problem occurred while trying to read the 
byte array.
+        */
+       public FluentByteArrayAssertion<BasicResource> assertBytes() throws 
IOException {
+               return new FluentByteArrayAssertion<>(asBytes(), this);
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Header convenience methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Tests if headers with the given name are contained within this 
resource (excluding those set on the entity itself).
+        *
+        * <p>
+        * Header name comparison is case insensitive.
+        *
+        * @param name The header name.
+        * @return <jk>true</jk> if at least one header with the name is 
present.
+        */
+       public boolean containsHeader(String name) {
+               return headers.contains(name);
+       }
+
+       /**
+        * Gets all of the headers with the given name (excluding those set on 
the entity itself).
+        *
+        * <p>
+        * The returned array maintains the relative order in which the headers 
were added.
+        *
+        * <p>
+        * Header name comparison is case insensitive.
+        *
+        * @param name The header name.
+        *
+        * @return An array containing all matching headers, or an empty array 
if none are found.
+        */
+       public List<Header> getHeaders(String name) {
+               return headers.get(name);
+       }
+
+       /**
+        * Gets the first header with the given name (excluding those set on 
the entity itself).
+        *
+        * <p>
+        * Header name comparison is case insensitive.
+        *
+        * @param name The header name.
+        * @return The first matching header, or <jk>null</jk> if not found.
+        */
+       public Header getFirstHeader(String name) {
+               return headers.getFirst(name);
+       }
+
+       /**
+        * Gets the last header with the given name (excluding those set on the 
entity itself).
+        *
+        * <p>
+        * Header name comparison is case insensitive.
+        *
+        * @param name The header name.
+        * @return The last matching header, or <jk>null</jk> if not found.
+        */
+       public Header getLastHeader(String name) {
+               return headers.getLast(name);
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Path-through methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Override /* HttpEntity */
+       public long getContentLength() {
+               return entity.getContentLength();
+       }
+
+       @Override /* HttpEntity */
+       public boolean isRepeatable() {
+               return entity.isRepeatable();
+       }
+
+       @Override /* HttpEntity */
+       public boolean isChunked() {
+               return entity.isChunked();
+       }
+
+       @Override /* HttpEntity */
+       public Header getContentType() {
+               return entity.getContentType();
+       }
+
+       @Override /* HttpEntity */
+       public Header getContentEncoding() {
+               return entity.getContentEncoding();
+       }
+
+       @Override /* HttpEntity */
+       public boolean isStreaming() {
+               return entity.isStreaming();
+       }
+
+       @Override
+       public void consumeContent() throws IOException {}
+
+       @Override /* HttpEntity */
+       public InputStream getContent() throws IOException, 
UnsupportedOperationException {
+               return entity.getContent();
+       }
+
+       @Override /* HttpEntity */
+       public void writeTo(OutputStream outStream) throws IOException {
+               entity.writeTo(outStream);
+       }
+
+       @Override /* HttpResource */
+       public List<Header> getHeaders() {
+               return headers.getAll();
+       }
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/ByteArrayResource.java
similarity index 64%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
copy to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/ByteArrayResource.java
index 8971339..edcd6e8 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/ByteArrayResource.java
@@ -10,35 +10,40 @@
 // * "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;
-
-import java.util.*;
+package org.apache.juneau.http.resource;
 
 import org.apache.juneau.http.entity.*;
 
 /**
- * API for retrieving localized static files from either the classpath or file 
system.
+ * A repeatable resource that obtains its content from a byte array.
  */
-public interface StaticFiles {
+public class ByteArrayResource extends BasicResource {
 
-       /** Represents no static files */
-       public abstract class Null implements StaticFiles {}
+       /**
+        * Creates a new {@link ByteArrayResource} builder.
+        *
+        * @return A new {@link ByteArrayResource} builder.
+        */
+       public static HttpResourceBuilder<ByteArrayResource> create() {
+               return new HttpResourceBuilder<>(ByteArrayResource.class, 
ByteArrayEntity.class);
+       }
 
        /**
-        * Creates a new builder for this object.
+        * Constructor.
         *
-        * @return A new builder for this object.
+        * @param builder The resource builder.
         */
-       public static StaticFilesBuilder create() {
-               return new StaticFilesBuilder();
+       public ByteArrayResource(HttpResourceBuilder<?> builder) {
+               super(builder);
        }
 
        /**
-        * Resolve the specified path.
+        * Creates a new {@link ByteArrayResource} builder initialized with the 
contents of this entity.
         *
-        * @param path The path to resolve to a static file.
-        * @param locale Optional locale.
-        * @return The resource, or <jk>null</jk> if not found.
+        * @return A new {@link ByteArrayResource} builder initialized with the 
contents of this entity.
         */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale);
+       @Override /* BasicResource */
+       public HttpResourceBuilder<ByteArrayResource> copy() {
+               return new HttpResourceBuilder<>(this);
+       }
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/FileResource.java
similarity index 64%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
copy to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/FileResource.java
index 8971339..7775190 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/FileResource.java
@@ -10,35 +10,41 @@
 // * "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;
-
-import java.util.*;
+package org.apache.juneau.http.resource;
 
+import java.io.*;
 import org.apache.juneau.http.entity.*;
 
 /**
- * API for retrieving localized static files from either the classpath or file 
system.
+ * A repeatable resource that obtains its content from a {@link File}.
  */
-public interface StaticFiles {
+public class FileResource extends BasicResource {
 
-       /** Represents no static files */
-       public abstract class Null implements StaticFiles {}
+       /**
+        * Creates a new {@link FileResource} builder.
+        *
+        * @return A new {@link FileResource} builder.
+        */
+       public static HttpResourceBuilder<FileResource> create() {
+               return new HttpResourceBuilder<>(FileResource.class, 
FileEntity.class);
+       }
 
        /**
-        * Creates a new builder for this object.
+        * Constructor.
         *
-        * @return A new builder for this object.
+        * @param builder The resource builder.
         */
-       public static StaticFilesBuilder create() {
-               return new StaticFilesBuilder();
+       public FileResource(HttpResourceBuilder<?> builder) {
+               super(builder);
        }
 
        /**
-        * Resolve the specified path.
+        * Creates a new {@link FileResource} builder initialized with the 
contents of this entity.
         *
-        * @param path The path to resolve to a static file.
-        * @param locale Optional locale.
-        * @return The resource, or <jk>null</jk> if not found.
+        * @return A new {@link FileResource} builder initialized with the 
contents of this entity.
         */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale);
+       @Override /* BasicResource */
+       public HttpResourceBuilder<FileResource> copy() {
+               return new HttpResourceBuilder<>(this);
+       }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpResource.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/HttpResource.java
similarity index 67%
rename from 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpResource.java
rename to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/HttpResource.java
index c73f36b..b0b2d39 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/entity/HttpResource.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/HttpResource.java
@@ -10,7 +10,7 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the 
License.                                              *
 // 
***************************************************************************************************************************
-package org.apache.juneau.http.entity;
+package org.apache.juneau.http.resource;
 
 import java.util.*;
 
@@ -20,13 +20,33 @@ import org.apache.juneau.http.annotation.*;
 
 /**
  * An extension of an {@link HttpEntity} that also includes arbitrary headers.
+ *
+ * <p>
+ * While {@link HttpEntity} beans support <c>Content-Type</c>, 
<c>Content-Encoding</c>, and <c>Content-Length</c>
+ * headers, this interface allows you to add any number of arbitrary headers 
to an entity.
+ *
+ * <p>
+ * For example, you might want to be able to create an entity with a 
<c>Cache-Control</c> header.
+ *
+ * <p class='bcode w800'>
+ *     <jk>import static</jk> org.apache.juneau.http.HttpResources.*;
+ *
+ *     <jc>// Create a resource with dynamic content and a no-cache 
header.</jc>
+ *     HttpResource <jv>myResource</jv> = 
<jsm>stringResource</jsm>(()-&gt;<jsm>getMyContent</jsm>(), 
ContentType.<jsf>TEXT_PLAIN</jsf>)
+ *             .header(<js>"Cache-Control"</js>, <js>"no-cache"</js>)
+ *             .build();
+ * </p>
  */
 @Response
 public interface HttpResource extends HttpEntity {
 
        /**
         * Returns the list of headers associated with this resource.
-        *
+        * 
+        * <p>
+        * Note that this typically does NOT include headers associated with 
{@link HttpEntity}
+        * (e.g. <c>Content-Type</c>, <c>Content-Encoding</c>, and 
<c>Content-Length</c>).
+        * 
         * @return The list of headers associated with this resource.
         */
        @ResponseHeader("*")
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/HttpResourceBuilder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/HttpResourceBuilder.java
new file mode 100644
index 0000000..b7332f4
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/HttpResourceBuilder.java
@@ -0,0 +1,477 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.http.resource;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.function.*;
+
+import org.apache.http.*;
+import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.header.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Builder for {@link HttpEntity} beans.
+ *
+ * @param <T> The bean type to create for this builder.
+ */
+@FluentSetters(returns="HttpResourceBuilder<T>")
+public class HttpResourceBuilder<T extends BasicResource> {
+
+       HeaderList headers = HeaderList.EMPTY;
+       HeaderListBuilder headersBuilder;
+
+       BasicHttpEntity entity;
+       HttpEntityBuilder<?> entityBuilder;
+
+       /** The HttpEntity implementation class. */
+       protected final Class<? extends BasicResource> implClass;
+
+       /** The HttpEntity implementation class. */
+       protected final Class<? extends BasicHttpEntity> entityImplClass;
+
+       /**
+        * Constructor.
+        *
+        * @param implClass
+        *      The subclass of {@link HttpResponse} to create.
+        *      <br>This must contain a public constructor that takes in an 
{@link HttpResourceBuilder} object.
+        * @param entityImplClass
+        *      The subclass of {@link BasicHttpEntity} to create.
+        *      <br>This must contain a public constructor that takes in an 
{@link HttpEntityBuilder} object.
+        */
+       public HttpResourceBuilder(Class<T> implClass, Class<? extends 
BasicHttpEntity> entityImplClass) {
+               this.implClass = implClass;
+               this.entityImplClass = entityImplClass;
+       }
+
+       /**
+        * Copy constructor.
+        *
+        * @param impl
+        *      The implementation object of {@link HttpEntity} to copy from.
+        *      <br>This must contain a public constructor that takes in an 
{@link HttpResourceBuilder} object.
+        */
+       public HttpResourceBuilder(T impl) {
+               implClass = impl.getClass();
+               headers = impl.headers;
+               entity = impl.entity;
+               this.entityImplClass = entity.getClass();
+       }
+
+       /**
+        * Instantiates the entity bean from the settings in this builder.
+        *
+        * @return A new {@link HttpEntity} bean.
+        */
+       @SuppressWarnings("unchecked")
+       public T build() {
+               try {
+                       return (T) 
implClass.getConstructor(HttpResourceBuilder.class).newInstance(this);
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       HeaderList headers() {
+               if (headersBuilder != null)
+                       return headersBuilder.build();
+               if (headers == null)
+                       return HeaderList.EMPTY;
+               return headers;
+       }
+
+       BasicHttpEntity entity() {
+               if (entityBuilder != null)
+                       return entityBuilder.build();
+               if (entity == null)
+                       return BasicHttpEntity.EMPTY;
+               return entity;
+       }
+
+       /**
+        * Copies the contents of the specified HTTP response to this builder.
+        *
+        * @param response The response to copy from.  Must not be null.
+        * @return This object (for method chaining).
+        * @throws IOException If content could not be retrieved.
+        */
+       public HttpResourceBuilder<?> copyFrom(HttpResponse response) throws 
IOException {
+               headers(response.getAllHeaders());
+               content(response.getEntity().getContent());
+               return this;
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // HttpEntityBuilder setters.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Sets the content on this entity bean.
+        *
+        * @param value The entity content, can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> content(Object value) {
+               entityBuilder().content(value);
+               return this;
+       }
+
+       /**
+        * Sets the content on this entity bean from a supplier.
+        *
+        * <p>
+        * Repeatable entities such as {@link StringEntity} use this to allow 
the entity content to be resolved at
+        * serialization time.
+        *
+        * @param value The entity content, can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> contentSupplier(Supplier<?> value) {
+               entityBuilder().contentSupplier(value);
+               return this;
+       }
+
+       /**
+        * Sets the content type on this entity bean.
+        *
+        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> contentType(String value) {
+               entityBuilder().contentType(value);
+               return this;
+       }
+
+       /**
+        * Sets the content type on this entity bean.
+        *
+        * @param value The new <c>Content-Type</ header, or <jk>null</jk> to 
unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> contentType(ContentType value) {
+               entityBuilder().contentType(value);
+               return this;
+       }
+
+       /**
+        * Sets the content length on this entity bean.
+        *
+        * @param value The new <c>Content-Length</c> header value, or 
<c>-1</c> to unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> contentLength(long value) {
+               entityBuilder().contentLength(value);
+               return this;
+       }
+
+       /**
+        * Sets the content encoding header on this entity bean.
+        *
+        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> contentEncoding(String value) {
+               entityBuilder().contentEncoding(value);
+               return this;
+       }
+
+       /**
+        * Sets the content encoding header on this entity bean.
+        *
+        * @param value The new <c>Content-Encoding</ header, or <jk>null</jk> 
to unset.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> contentEncoding(ContentEncoding value) {
+               entityBuilder().contentEncoding(value);
+               return this;
+       }
+
+       /**
+        * Sets the 'chunked' flag value to <jk>true</jk>.
+        *
+        * <ul class='notes'>
+        *      <li>If the {@link HttpEntity#getContentLength()} method returns 
a negative value, the HttpClient code will always
+        *              use chunked encoding.
+        * </ul>
+        *
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> chunked() {
+               entityBuilder().chunked();
+               return this;
+       }
+
+       /**
+        * Sets the 'chunked' flag value.
+        *
+        * <ul class='notes'>
+        *      <li>If the {@link HttpEntity#getContentLength()} method returns 
a negative value, the HttpClient code will always
+        *              use chunked encoding.
+        * </ul>
+        *
+        * @param value The new value for this flag.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> chunked(boolean value) {
+               entityBuilder().chunked(value);
+               return this;
+       }
+
+       /**
+        * Specifies that the contents of this resource should be cached into 
an internal byte array so that it can
+        * be read multiple times.
+        *
+        * @return This object (for method chaining).
+        * @throws IOException If entity could not be read into memory.
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> cached() throws IOException {
+               entityBuilder().cached();
+               return this;
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // BasicHeaderGroup setters.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Sets the protocol version on the status line.
+        *
+        * <p>
+        * If not specified, <js>"HTTP/1.1"</js> will be used.
+        *
+        * @param value The new value.
+        * @return This object (for method chaining).
+        */
+       @FluentSetter
+       public HttpResourceBuilder<T> headerList(HeaderList value) {
+               headers = value;
+               headersBuilder = null;
+               return this;
+       }
+
+       /**
+        * Removes any headers already in this builder.
+        *
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> clearHeaders() {
+               headersBuilder().clear();
+               return this;
+       }
+
+       /**
+        * Adds the specified header to the end of the headers in this builder.
+        *
+        * @param value The header to add.  <jk>null</jk> values are ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> header(Header value) {
+               if (value != null)
+                       headersBuilder().add(value);
+               return this;
+       }
+
+       /**
+        * Adds the specified header to the end of the headers in this builder.
+        *
+        * <p>
+        * This is a no-op if either the name or value is <jk>null</jk>.
+        *
+        * @param name The header name.
+        * @param value The header value.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> header(String name, String value) {
+               if (name != null && value != null)
+                       headersBuilder().add(name, value);
+               return this;
+       }
+
+       /**
+        * Adds the specified headers to the end of the headers in this builder.
+        *
+        * @param values The headers to add.  <jk>null</jk> headers and headers 
with <jk>null</jk> names or values are ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> headers(Header...values) {
+               for (Header h : values) {
+                       if (h != null) {
+                               String n = h.getName();
+                               String v = h.getValue();
+                               if (isNotEmpty(n)) {
+                                       if (n.equalsIgnoreCase("content-type"))
+                                               contentType(v);
+                                       else if 
(n.equalsIgnoreCase("content-encoding"))
+                                               contentEncoding(v);
+                                       else if 
(n.equalsIgnoreCase("content-length"))
+                                               
contentLength(Long.parseLong(v));
+                                       else
+                                               headersBuilder().add(h);
+                               }
+                       }
+               }
+               return this;
+       }
+
+       /**
+        * Adds the specified headers to the end of the headers in this builder.
+        *
+        * @param values The headers to add.  <jk>null</jk> values are ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> headers(List<Header> values) {
+               headersBuilder().add(values);
+               return this;
+       }
+
+       /**
+        * Removes the specified header from this builder.
+        *
+        * @param value The header to remove.  <jk>null</jk> values are ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> removeHeader(Header value) {
+               headersBuilder().remove(value);
+               return this;
+       }
+
+       /**
+        * Removes the specified headers from this builder.
+        *
+        * @param values The headers to remove.  <jk>null</jk> values are 
ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> removeHeaders(Header...values) {
+               headersBuilder().remove(values);
+               return this;
+       }
+
+       /**
+        * Removes the specified headers from this builder.
+        *
+        * @param values The headers to remove.  <jk>null</jk> values are 
ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> removeHeaders(List<Header> values) {
+               headersBuilder().remove(values);
+               return this;
+       }
+
+       /**
+        * Replaces the first occurrence of the header with the same name.
+        *
+        * <p>
+        * If no header with the same name is found the given header is added 
to the end of the list.
+        *
+        * @param value The headers to replace.  <jk>null</jk> values are 
ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> updateHeader(Header value) {
+               headersBuilder().update(value);
+               return this;
+       }
+
+       /**
+        * Replaces the first occurrence of the headers with the same name.
+        *
+        * <p>
+        * If no header with the same name is found the given header is added 
to the end of the list.
+        *
+        * @param values The headers to replace.  <jk>null</jk> values are 
ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> updateHeaders(Header...values) {
+               headersBuilder().update(values);
+               return this;
+       }
+
+       /**
+        * Replaces the first occurrence of the headers with the same name.
+        *
+        * <p>
+        * If no header with the same name is found the given header is added 
to the end of the list.
+        *
+        * @param values The headers to replace.  <jk>null</jk> values are 
ignored.
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> updateHeaders(List<Header> values) {
+               headersBuilder().update(values);
+               return this;
+       }
+
+       /**
+        * Sets all of the headers contained within this group overriding any 
existing headers.
+        *
+        * <p>
+        * The headers are added in the order in which they appear in the array.
+        *
+        * @param values The headers to set
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> setHeaders(Header...values) {
+               headersBuilder().set(values);
+               return this;
+       }
+
+       /**
+        * Sets all of the headers contained within this group overriding any 
existing headers.
+        *
+        * <p>
+        * The headers are added in the order in which they appear in the list.
+        *
+        * @param values The headers to set
+        * @return This object (for method chaining).
+        */
+       public HttpResourceBuilder<T> setHeaders(List<Header> values) {
+               headersBuilder().set(values);
+               return this;
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Other methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       private HeaderListBuilder headersBuilder() {
+               if (headersBuilder == null) {
+                       headersBuilder = headers == null ? HeaderList.create() 
: headers.builder();
+                       headers = null;
+               }
+               return headersBuilder;
+       }
+
+       private HttpEntityBuilder<?> entityBuilder() {
+               if (entityBuilder == null) {
+                       entityBuilder = entity == null ? 
BasicHttpEntity.create(entityImplClass) : entity.copy();
+                       entity = null;
+               }
+               return entityBuilder;
+       }
+
+       // <FluentSetters>
+
+       // </FluentSetters>
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/InputStreamResource.java
similarity index 62%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
copy to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/InputStreamResource.java
index 8971339..ea98c71 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/InputStreamResource.java
@@ -10,35 +10,41 @@
 // * "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;
-
-import java.util.*;
+package org.apache.juneau.http.resource;
 
+import java.io.*;
 import org.apache.juneau.http.entity.*;
 
 /**
- * API for retrieving localized static files from either the classpath or file 
system.
+ * A streamed, non-repeatable resource that obtains its content from an {@link 
InputStream}.
  */
-public interface StaticFiles {
+public class InputStreamResource extends BasicResource {
 
-       /** Represents no static files */
-       public abstract class Null implements StaticFiles {}
+       /**
+        * Creates a new {@link InputStreamResource} builder.
+        *
+        * @return A new {@link InputStreamResource} builder.
+        */
+       public static HttpResourceBuilder<InputStreamResource> create() {
+               return new HttpResourceBuilder<>(InputStreamResource.class, 
InputStreamEntity.class);
+       }
 
        /**
-        * Creates a new builder for this object.
+        * Constructor.
         *
-        * @return A new builder for this object.
+        * @param builder The resource builder.
         */
-       public static StaticFilesBuilder create() {
-               return new StaticFilesBuilder();
+       public InputStreamResource(HttpResourceBuilder<?> builder) {
+               super(builder);
        }
 
        /**
-        * Resolve the specified path.
+        * Creates a new {@link InputStreamResource} builder initialized with 
the contents of this entity.
         *
-        * @param path The path to resolve to a static file.
-        * @param locale Optional locale.
-        * @return The resource, or <jk>null</jk> if not found.
+        * @return A new {@link InputStreamResource} builder initialized with 
the contents of this entity.
         */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale);
+       @Override /* BasicResource */
+       public HttpResourceBuilder<InputStreamResource> copy() {
+               return new HttpResourceBuilder<>(this);
+       }
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/ReaderResource.java
similarity index 63%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
copy to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/ReaderResource.java
index 8971339..d12ee12 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/ReaderResource.java
@@ -10,35 +10,41 @@
 // * "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;
-
-import java.util.*;
+package org.apache.juneau.http.resource;
 
+import java.io.*;
 import org.apache.juneau.http.entity.*;
 
 /**
- * API for retrieving localized static files from either the classpath or file 
system.
+ * A streamed, non-repeatable resource that obtains its content from an {@link 
Reader}.
  */
-public interface StaticFiles {
+public class ReaderResource extends BasicResource {
 
-       /** Represents no static files */
-       public abstract class Null implements StaticFiles {}
+       /**
+        * Creates a new {@link InputStreamResource} builder.
+        *
+        * @return A new {@link InputStreamResource} builder.
+        */
+       public static HttpResourceBuilder<ReaderResource> create() {
+               return new HttpResourceBuilder<>(ReaderResource.class, 
ReaderEntity.class);
+       }
 
        /**
-        * Creates a new builder for this object.
+        * Constructor.
         *
-        * @return A new builder for this object.
+        * @param builder The resource builder.
         */
-       public static StaticFilesBuilder create() {
-               return new StaticFilesBuilder();
+       public ReaderResource(HttpResourceBuilder<?> builder) {
+               super(builder);
        }
 
        /**
-        * Resolve the specified path.
+        * Creates a new {@link ReaderResource} builder initialized with the 
contents of this entity.
         *
-        * @param path The path to resolve to a static file.
-        * @param locale Optional locale.
-        * @return The resource, or <jk>null</jk> if not found.
+        * @return A new {@link ReaderResource} builder initialized with the 
contents of this entity.
         */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale);
+       @Override /* BasicResource */
+       public HttpResourceBuilder<ReaderResource> copy() {
+               return new HttpResourceBuilder<>(this);
+       }
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/StringResource.java
similarity index 64%
copy from 
juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
copy to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/StringResource.java
index 8971339..c5fdf16 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/resource/StringResource.java
@@ -10,35 +10,41 @@
 // * "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;
-
-import java.util.*;
+package org.apache.juneau.http.resource;
 
 import org.apache.juneau.http.entity.*;
 
 /**
- * API for retrieving localized static files from either the classpath or file 
system.
+ * A self contained, repeatable resource that obtains its content from a 
{@link String}.
  */
-public interface StaticFiles {
+public class StringResource extends BasicResource {
 
-       /** Represents no static files */
-       public abstract class Null implements StaticFiles {}
+       /**
+        * Creates a new {@link InputStreamResource} builder.
+        *
+        * @return A new {@link InputStreamResource} builder.
+        */
+       public static HttpResourceBuilder<StringResource> create() {
+               return new HttpResourceBuilder<>(StringResource.class, 
StringEntity.class);
+       }
 
        /**
-        * Creates a new builder for this object.
+        * Constructor.
         *
-        * @return A new builder for this object.
+        * @param builder The resource builder.
         */
-       public static StaticFilesBuilder create() {
-               return new StaticFilesBuilder();
+       public StringResource(HttpResourceBuilder<?> builder) {
+               super(builder);
        }
 
        /**
-        * Resolve the specified path.
+        * Creates a new {@link StringResource} builder initialized with the 
contents of this entity.
         *
-        * @param path The path to resolve to a static file.
-        * @param locale Optional locale.
-        * @return The resource, or <jk>null</jk> if not found.
+        * @return A new {@link StringResource} builder initialized with the 
contents of this entity.
         */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale);
+       @Override /* BasicResource */
+       public HttpResourceBuilder<StringResource> copy() {
+               return new HttpResourceBuilder<>(this);
+       }
 }
+
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
index 8b310e9..0680ddd 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
@@ -27,6 +27,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.header.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.oapi.*;
@@ -314,8 +315,8 @@ public class ResponseBody implements HttpEntity {
        public byte[] asBytes() throws RestCallException {
                if (body == null) {
                        try {
-                               if (entity instanceof BasicHttpEntity2) {
-                                       body = 
((BasicHttpEntity2)entity).asBytes();
+                               if (entity instanceof BasicHttpEntity) {
+                                       body = 
((BasicHttpEntity)entity).asBytes();
                                } else {
                                        body = readBytes(entity.getContent());
                                }
@@ -522,7 +523,8 @@ public class ResponseBody implements HttpEntity {
         *                      <li>{@link ResponseBody}/{@link HttpEntity} - 
Returns access to this object.
         *                      <li>{@link Reader} - Returns access to the raw 
reader of the response.
         *                      <li>{@link InputStream} - Returns access to the 
raw input stream of the response.
-        *                      <li>{@link BasicHttpResource} - Raw contents 
will be serialized to remote resource.  Additional headers and media type will 
be set on request.
+        *                      <li>{@link HttpResource} - Response will be 
converted to an {@link BasicResource}.
+        *                      <li>Any type that takes in an {@link 
HttpResponse} object.
         *              </ul>
         *      <li>
         *              If {@link #cache()} or {@link RestResponse#cacheBody()} 
has been called, this method can be can be called multiple times and/or 
combined with
@@ -583,7 +585,8 @@ public class ResponseBody implements HttpEntity {
         *                      <li>{@link ResponseBody}/{@link HttpEntity} - 
Returns access to this object.
         *                      <li>{@link Reader} - Returns access to the raw 
reader of the response.
         *                      <li>{@link InputStream} - Returns access to the 
raw input stream of the response.
-        *                      <li>{@link BasicHttpResource} - Raw contents 
will be serialized to remote resource.  Additional headers and media type will 
be set on request.
+        *                      <li>{@link HttpResource} - Response will be 
converted to an {@link BasicResource}.
+        *                      <li>Any type that takes in an {@link 
HttpResponse} object.
         *              </ul>
         *      <li>
         *              If {@link #cache()} or {@link RestResponse#cacheBody()} 
has been called, this method can be can be called multiple times and/or 
combined with
@@ -668,6 +671,9 @@ public class ResponseBody implements HttpEntity {
                        if (type.is(HttpResponse.class))
                                return (T)response;
 
+                       if (type.is(HttpResource.class))
+                               type = 
(ClassMeta<T>)getClassMeta(BasicResource.class);
+                               
                        ConstructorInfo ci = 
type.getInfo().getPublicConstructor(HttpResponse.class);
                        if (ci != null) {
                                try {
@@ -677,19 +683,6 @@ public class ResponseBody implements HttpEntity {
                                }
                        }
 
-                       if (type.isChildOf(HttpResource.class)) {
-                               BasicHttpResource r = 
BasicHttpResource.of(asInputStream());
-                               for (Header h : response.getAllHeaders()) {
-                                       if 
(h.getName().equalsIgnoreCase("Content-Type"))
-                                               r.contentType(h);
-                                       else if 
(h.getName().equalsIgnoreCase("Content-Encoding"))
-                                               r.contentEncoding(h);
-                                       else
-                                               r.header(h);
-                               }
-                               return (T)r;
-                       }
-
                        String ct = 
firstNonEmpty(response.getResponseHeader("Content-Type").orElse("text/plain"));
 
                        if (parser == null)
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index 77935db..f16e872 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -52,6 +52,7 @@ import org.apache.juneau.annotation.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.cp.*;
 import org.apache.juneau.http.remote.RemoteReturn;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.part.*;
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
index ce96771..99ec883 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
@@ -40,9 +40,9 @@ import org.apache.http.protocol.*;
 import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.html.*;
-import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.part.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.msgpack.*;
@@ -1801,7 +1801,7 @@ public class RestRequest extends BeanSession implements 
HttpUriRequest, Configur
         *              <li>
         *                      {@link InputStream} - Raw contents of {@code 
InputStream} will be serialized to remote resource.
         *              <li>
-        *                      {@link HttpResource}/{@link BasicHttpResource} 
- Raw contents will be serialized to remote resource.  Additional headers and 
media type will be set on request.
+        *                      {@link HttpResource} - Raw contents will be 
serialized to remote resource.  Additional headers and media type will be set 
on request.
         *              <li>
         *                      {@link HttpEntity}/{@link BasicHttpEntity} - 
Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
         *              <li>
@@ -1891,7 +1891,7 @@ public class RestRequest extends BeanSession implements 
HttpUriRequest, Configur
         *              <li>
         *                      {@link InputStream} - Raw contents of {@code 
InputStream} will be serialized to remote resource.
         *              <li>
-        *                      {@link HttpResource}/{@link BasicHttpResource} 
- Raw contents will be serialized to remote resource.  Additional headers and 
media type will be set on request.
+        *                      {@link HttpResource} - Raw contents will be 
serialized to remote resource.  Additional headers and media type will be set 
on request.
         *              <li>
         *                      {@link HttpEntity}/{@link BasicHttpEntity} - 
Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
         *              <li>
@@ -1960,7 +1960,7 @@ public class RestRequest extends BeanSession implements 
HttpUriRequest, Configur
         *              <li>
         *                      {@link InputStream} - Raw contents of {@code 
InputStream} will be serialized to remote resource.
         *              <li>
-        *                      {@link HttpResource}/{@link BasicHttpResource} 
- Raw contents will be serialized to remote resource.  Additional headers and 
media type will be set on request.
+        *                      {@link HttpResource} - Raw contents will be 
serialized to remote resource.  Additional headers and media type will be set 
on request.
         *              <li>
         *                      {@link HttpEntity}/{@link BasicHttpEntity} - 
Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
         *              <li>
diff --git 
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/BasicSpringRestServlet.java
 
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/BasicSpringRestServlet.java
index 54bc8a7..caada64 100644
--- 
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/BasicSpringRestServlet.java
+++ 
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/BasicSpringRestServlet.java
@@ -18,7 +18,7 @@ import org.apache.juneau.dto.swagger.Swagger;
 import org.apache.juneau.html.*;
 import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.jso.*;
 import org.apache.juneau.json.*;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestObject.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestObject.java
index 80a9fd6..8bc1ea5 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestObject.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestObject.java
@@ -20,7 +20,7 @@ import org.apache.juneau.dto.swagger.Swagger;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.config.*;
 import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.response.*;
 
 /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestOperations.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestOperations.java
index c330854..9f4889b 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestOperations.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestOperations.java
@@ -21,7 +21,7 @@ import org.apache.juneau.dto.swagger.Swagger;
 import org.apache.juneau.dto.swagger.ui.*;
 import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.jsonschema.annotation.*;
 import org.apache.juneau.rest.annotation.*;
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
index bfa7a0c..9c8beac 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
@@ -18,7 +18,7 @@ import org.apache.juneau.dto.swagger.Swagger;
 import org.apache.juneau.html.*;
 import org.apache.juneau.html.annotation.*;
 import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.jso.*;
 import org.apache.juneau.json.*;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicStaticFiles.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicStaticFiles.java
index 95e7e33..8cd5c82 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicStaticFiles.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicStaticFiles.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest;
 
 import static org.apache.juneau.http.HttpHeaders.*;
+import static org.apache.juneau.http.HttpResources.*;
 import static org.apache.juneau.internal.FileUtils.*;
 import static org.apache.juneau.internal.ObjectUtils.*;
 
@@ -24,7 +25,7 @@ import javax.activation.*;
 import org.apache.http.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.cp.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.internal.*;
 
@@ -32,7 +33,7 @@ import org.apache.juneau.internal.*;
  * API for retrieving localized static files from either the classpath or file 
system.
  *
  * <p>
- * Provides the same functionality as {@link BasicFileFinder} but adds support 
for returning files as {@link BasicHttpResource}
+ * Provides the same functionality as {@link BasicFileFinder} but adds support 
for returning files as {@link HttpResource}
  * objects with arbitrary headers.
  */
 public class BasicStaticFiles extends BasicFileFinder implements StaticFiles {
@@ -86,16 +87,16 @@ public class BasicStaticFiles extends BasicFileFinder 
implements StaticFiles {
         * @return The resource, or <jk>null</jk> if not found.
         */
        @Override /* StaticFiles */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale) {
+       public Optional<HttpResource> resolve(String path, Locale locale) {
                try {
                        Optional<InputStream> is = getStream(path);
                        if (! is.isPresent())
                                return Optional.empty();
                        return Optional.of(
-                               BasicHttpResource
-                                       .of(is.get())
+                               streamResource(is.get())
                                        .header(contentType(mimeTypes == null ? 
null : mimeTypes.getContentType(getFileName(path))))
                                        .headers(headers)
+                                       .build()
                        );
                } catch (IOException e) {
                        throw new InternalServerError(e);
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
index 8971339..bec3c69 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
@@ -14,7 +14,7 @@ package org.apache.juneau.rest;
 
 import java.util.*;
 
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 
 /**
  * API for retrieving localized static files from either the classpath or file 
system.
@@ -40,5 +40,5 @@ public interface StaticFiles {
         * @param locale Optional locale.
         * @return The resource, or <jk>null</jk> if not found.
         */
-       public Optional<BasicHttpResource> resolve(String path, Locale locale);
+       public Optional<HttpResource> resolve(String path, Locale locale);
 }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
index 46e2dc3..5a5b85e 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/http/BasicHttpResource_Test.java
@@ -14,121 +14,109 @@ package org.apache.juneau.http;
 
 import static org.apache.juneau.assertions.Assertions.*;
 import static org.apache.juneau.http.HttpHeaders.*;
-import static org.apache.juneau.http.entity.BasicHttpResource.*;
+import static org.apache.juneau.http.HttpResources.*;
 import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
 
 import java.io.*;
 
 import org.apache.juneau.collections.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.header.*;
 import org.junit.*;
 
-@SuppressWarnings("deprecation")
+@FixMethodOrder(NAME_ASCENDING)
 public class BasicHttpResource_Test {
        @Test
        public void a01_basic() throws Exception {
-               BasicHttpResource x = of(null);
                File f = File.createTempFile("test", "txt");
 
+               HttpResource x = stringResource((String)null).build();
+
                assertNull(x.getContentType());
-               assertNull(x.getContent());
+               assertStream(x.getContent()).isNotNull().asString().isEmpty();
                assertNull(x.getContentEncoding());
                assertList(x.getHeaders()).isSize(0);
 
-               x = of("foo");
+               x = stringResource("foo").build();
                assertStream(x.getContent()).asString().is("foo");
                assertTrue(x.isRepeatable());
                assertFalse(x.isStreaming());
 
-               x = of(new StringReader("foo"));
+               x = readerResource(new StringReader("foo")).build();
                assertStream(x.getContent()).asString().is("foo");
                assertFalse(x.isRepeatable());
                assertTrue(x.isStreaming());
 
-               x = of("foo".getBytes());
+               x = byteArrayResource("foo".getBytes()).build();
                assertStream(x.getContent()).asString().is("foo");
                assertTrue(x.isRepeatable());
                assertFalse(x.isStreaming());
 
-               x = of(new ByteArrayInputStream("foo".getBytes()));
+               x = streamResource(new 
ByteArrayInputStream("foo".getBytes())).build();
                assertStream(x.getContent()).asString().is("foo");
                assertFalse(x.isRepeatable());
                assertTrue(x.isStreaming());
 
-               x = of(null);
-               assertStream(x.getContent()).asString().doesNotExist();
+               x = streamResource(null).build();
+               assertStream(x.getContent()).isNotNull().asString().isEmpty();
                assertFalse(x.isRepeatable());
-               assertFalse(x.isStreaming());
+               assertTrue(x.isStreaming());
 
-               x = of(f);
+               x = fileResource(f).build();
                assertStream(x.getContent()).asString().isEmpty();
                assertTrue(x.isRepeatable());
                assertFalse(x.isStreaming());
 
-               x = of("foo").cache();
+               x = stringResource("foo").cached().build();
                assertStream(x.getContent()).asString().is("foo");
                assertStream(x.getContent()).asString().is("foo");
                assertTrue(x.isRepeatable());
 
-               x = of(new StringReader("foo")).cache();
+               x = readerResource(new StringReader("foo")).cached().build();
                assertStream(x.getContent()).asString().is("foo");
                assertStream(x.getContent()).asString().is("foo");
                assertTrue(x.isRepeatable());
 
-               x = of("foo".getBytes()).cache();
+               x = byteArrayResource("foo".getBytes()).cached().build();
                assertStream(x.getContent()).asString().is("foo");
                assertStream(x.getContent()).asString().is("foo");
                assertTrue(x.isRepeatable());
 
-               x = of(new ByteArrayInputStream("foo".getBytes())).cache();
+               x = streamResource(new 
ByteArrayInputStream("foo".getBytes())).cached().build();
                assertStream(x.getContent()).asString().is("foo");
                assertStream(x.getContent()).asString().is("foo");
                assertTrue(x.isRepeatable());
 
-               x = of(null).cache();
-               assertStream(x.getContent()).asString().doesNotExist();
-               assertStream(x.getContent()).asString().doesNotExist();
+               x = stringResource((String)null).cached().build();
+               assertStream(x.getContent()).exists().asString().isEmpty();
                assertTrue(x.isRepeatable());
                x.writeTo(new ByteArrayOutputStream());
 
-               x = of(f).cache();
-               assertStream(x.getContent()).asString().isEmpty();
+               x = fileResource(f).cached().build();
                assertStream(x.getContent()).asString().isEmpty();
                assertTrue(x.isRepeatable());
                x.writeTo(new ByteArrayOutputStream());
 
-               assertLong(of("foo").getContentLength()).is(3l);
-               assertLong(of("foo".getBytes()).getContentLength()).is(3l);
-               assertLong(of(f).getContentLength()).is(0l);
+               
assertLong(stringResource("foo").build().getContentLength()).is(3l);
+               
assertLong(byteArrayResource("foo".getBytes()).build().getContentLength()).is(3l);
+               assertLong(fileResource(f).build().getContentLength()).is(0l);
 
-               assertLong(of(new 
StringReader("foo")).getContentLength()).is(-1l);
-               assertLong(of(new 
StringReader("foo")).contentLength(3).getContentLength()).is(3l);
+               assertLong(readerResource(new 
StringReader("foo")).build().getContentLength()).is(-1l);
+               assertLong(readerResource(new 
StringReader("foo")).contentLength(3).build().getContentLength()).is(3l);
 
-               x = new BasicHttpResource("foo", contentType("text/plain"), 
contentEncoding("identity"));
+               x = stringResource("foo", 
contentType("text/plain")).contentEncoding("identity").build();
                assertString(x.getContentType().getValue()).is("text/plain");
                assertString(x.getContentEncoding().getValue()).is("identity");
 
-               x = new BasicHttpResource("foo", null, null);
+               x = stringResource("foo", 
null).contentEncoding((String)null).build();
                assertObject(x.getContentType()).doesNotExist();
                assertObject(x.getContentEncoding()).doesNotExist();
-
-               BasicHttpResource x2 = new BasicHttpResource(new 
StringReader("foo")) {
-                       @Override
-                       protected byte[] readBytes(Object o) throws IOException 
{
-                               throw new IOException("bad");
-                       }
-               };
-               x2.cache();
-               assertLong(x2.getContentLength()).is(-1l);
-               assertThrown(()->x2.writeTo(new 
ByteArrayOutputStream())).contains("bad");
        }
 
        @Test
        public void a02_header_String_Object() throws Exception {
-               BasicHttpResource x = 
of("foo").header("Foo","bar").header("Foo","baz").header(null,"bar").header("foo",null);
-               assertString(x.getStringHeader("Foo")).is("baz");
-               assertString(x.getStringHeader("Bar")).doesNotExist();
+               StringResource x = 
stringResource("foo").header("Foo","bar").header("Foo","baz").header(null,"bar").header("foo",null).build();
                assertString(x.getFirstHeader("Foo").toString()).is("Foo: bar");
                assertString(x.getLastHeader("Foo").toString()).is("Foo: baz");
                assertObject(x.getFirstHeader("Bar")).doesNotExist();
@@ -138,70 +126,64 @@ public class BasicHttpResource_Test {
 
        @Test
        public void a03_header_Header() throws Exception {
-               BasicHttpResource x = 
of("foo").header(null).header(header("Foo","bar")).header(header("Foo","baz")).header(header(null,"bar")).header(header("Bar",null)).header(null);
-               assertString(x.getStringHeader("Foo")).is("baz");
-               assertString(x.getStringHeader("Bar")).doesNotExist();
+               StringResource x = 
stringResource("foo").header(null).header(header("Foo","bar")).header(header("Foo","baz")).header(header(null,"bar")).header(header("Bar",null)).header(null).build();
                assertString(x.getFirstHeader("Foo").toString()).is("Foo: bar");
                assertString(x.getLastHeader("Foo").toString()).is("Foo: baz");
                assertObject(x.getFirstHeader("Bar").getValue()).doesNotExist();
                assertObject(x.getLastHeader("Bar").getValue()).doesNotExist();
-               assertObject(x.getHeaders()).asJson().is("[null,'Foo: 
bar','Foo: baz','null: bar','Bar: null',null]");
+               assertObject(x.getHeaders()).asJson().is("['Foo: bar','Foo: 
baz','null: bar','Bar: null']");
        }
 
        @Test
        public void a04_headers_List() throws Exception {
-               BasicHttpResource x = 
of("foo").headers(AList.of(header("Foo","bar"),header("Foo","baz"),header(null,"bar"),header("Bar",null),null));
-               assertString(x.getStringHeader("Foo")).is("baz");
-               assertString(x.getStringHeader("Bar")).doesNotExist();
+               StringResource x = 
stringResource("foo").headers(AList.of(header("Foo","bar"),header("Foo","baz"),header(null,"bar"),header("Bar",null),null)).build();
                assertString(x.getFirstHeader("Foo").toString()).is("Foo: bar");
                assertString(x.getLastHeader("Foo").toString()).is("Foo: baz");
                assertObject(x.getFirstHeader("Bar").getValue()).doesNotExist();
                assertObject(x.getLastHeader("Bar").getValue()).doesNotExist();
-               assertObject(x.getHeaders()).asJson().is("['Foo: bar','Foo: 
baz','null: bar','Bar: null',null]");
+               assertObject(x.getHeaders()).asJson().is("['Foo: bar','Foo: 
baz','null: bar','Bar: null']");
        }
 
        @Test
        public void a05_headers_array() throws Exception {
-               BasicHttpResource x = 
of("foo").headers(header("Foo","bar"),header("Foo","baz"),header(null,"bar"),header("Bar",null),null);
-               assertString(x.getStringHeader("Foo")).is("baz");
-               assertString(x.getStringHeader("Bar")).doesNotExist();
+               StringResource x = 
stringResource("foo").headers(header("Foo","bar"),header("Foo","baz"),header(null,"bar"),header("Bar",null),null).build();
                assertString(x.getFirstHeader("Foo").toString()).is("Foo: bar");
                assertString(x.getLastHeader("Foo").toString()).is("Foo: baz");
                assertObject(x.getFirstHeader("Bar").getValue()).doesNotExist();
                assertObject(x.getLastHeader("Bar").getValue()).doesNotExist();
-               assertObject(x.getHeaders()).asJson().is("['Foo: bar','Foo: 
baz','null: bar','Bar: null',null]");
+               assertObject(x.getHeaders()).asJson().is("['Foo: bar','Foo: 
baz','Bar: null']");
        }
 
 
        @Test
        public void a06_chunked() throws Exception {
-               BasicHttpResource x1 = of("foo").chunked();
+               StringResource x1 = stringResource("foo").chunked().build();
                assertBoolean(x1.isChunked()).isTrue();
-               BasicHttpResource x2 = of("foo");
+               StringResource x2 = stringResource("foo").build();
                assertBoolean(x2.isChunked()).isFalse();
        }
 
        @Test
        public void a07_chunked_boolean() throws Exception {
-               BasicHttpResource x1 = of("foo").chunked(true);
+               StringResource x1 = stringResource("foo").chunked(true).build();
                assertBoolean(x1.isChunked()).isTrue();
-               BasicHttpResource x2 = of("foo").chunked(false);
+               StringResource x2 = 
stringResource("foo").chunked(false).build();
                assertBoolean(x2.isChunked()).isFalse();
        }
 
        @Test
        public void a08_contentType_String() throws Exception {
-               BasicHttpResource x1 = of("foo").contentType("text/plain");
+               StringResource x1 = 
stringResource("foo").contentType("text/plain").build();
                assertString(x1.getContentType().getValue()).is("text/plain");
-               BasicHttpResource x2 = of("foo").contentType((String)null);
+               StringResource x2 = 
stringResource("foo").contentType((String)null).build();
                assertObject(x2.getContentType()).doesNotExist();
        }
 
        @Test
        public void a09_contentEncoding_String() throws Exception {
-               BasicHttpResource x1 = of("foo").contentEncoding("identity");
+               StringResource x1 = 
stringResource("foo").contentEncoding("identity").build();
                assertString(x1.getContentEncoding().getValue()).is("identity");
-               BasicHttpResource x2 = of("foo").contentEncoding((String)null);
+               StringResource x2 = 
stringResource("foo").contentEncoding((String)null).build();
                assertObject(x2.getContentEncoding()).doesNotExist();
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_CommonInterfaces_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_CommonInterfaces_Test.java
index 1edb429..c97fc59 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_CommonInterfaces_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/http/remote/Remote_CommonInterfaces_Test.java
@@ -15,6 +15,7 @@ package org.apache.juneau.http.remote;
 import static org.apache.juneau.assertions.Assertions.*;
 import static org.apache.juneau.http.HttpHeaders.*;
 import static org.apache.juneau.http.HttpResponses.*;
+import static org.apache.juneau.http.HttpResources.*;
 import static org.junit.Assert.*;
 import static org.junit.runners.MethodSorters.*;
 import static org.apache.juneau.internal.IOUtils.*;
@@ -24,7 +25,7 @@ import java.io.*;
 import org.apache.juneau.http.annotation.Body;
 import org.apache.juneau.http.annotation.Header;
 import org.apache.juneau.http.annotation.Query;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.config.*;
 import org.apache.juneau.rest.helper.*;
@@ -170,7 +171,7 @@ public class Remote_CommonInterfaces_Test {
 
                // HttpClient goes into loop if status code is less than 200 so 
we can't test those.
 
-               C x = 
MockRestClient.create(C1.class).json().disableRedirectHandling().debug().build().getRemote(C.class);
+               C x = 
MockRestClient.create(C1.class).json().disableRedirectHandling().build().getRemote(C.class);
                assertObject(x.ok()).asString().contains("HTTP/1.1 200 OK");
                assertObject(x.accepted()).asString().contains("HTTP/1.1 202 
Accepted");
                assertObject(x.alreadyReported()).asString().contains("HTTP/1.1 
208 Already Reported");
@@ -198,24 +199,23 @@ public class Remote_CommonInterfaces_Test {
        @Remote
        @Rest
        public static interface D extends BasicSimpleJsonRest {
-               BasicHttpResource httpResource() throws IOException ;
+               BasicResource httpResource() throws IOException ;
        }
 
        public static class D1 implements D {
                @Override
-               public BasicHttpResource httpResource() throws IOException {
-                       return 
BasicHttpResource.of("foo".getBytes()).contentType("text/foo").header("Foo","foo").headers(eTag("\"bar\""));
+               public BasicResource httpResource() throws IOException {
+                       return 
byteArrayResource("foo".getBytes()).contentType("text/foo").header("Foo","foo").headers(eTag("\"bar\"")).build();
                }
        }
 
-       @SuppressWarnings("deprecation")
        @Test
        public void d01_httpResource() throws Exception {
                D x = MockRestClient.build(D1.class).getRemote(D.class);
-               BasicHttpResource sr = x.httpResource();
+               BasicResource sr = x.httpResource();
                assertEquals("foo",read(sr.getContent()));
-               assertEquals("foo",sr.getStringHeader("Foo"));
-               assertEquals("\"bar\"",sr.getStringHeader("ETag"));
+               assertEquals("foo",sr.getLastHeader("Foo").getValue());
+               assertEquals("\"bar\"",sr.getLastHeader("ETag").getValue());
                
assertEquals("text/foo",sr.getContentType().getValue().toString());
        }
 
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Returns_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Returns_Test.java
index c555869..17a3b2e 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Returns_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/RestOp_Returns_Test.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest;
 
 import static org.apache.juneau.http.HttpResponses.*;
+import static org.apache.juneau.http.HttpResources.*;
 import static org.apache.juneau.rest.testutils.TestUtils.*;
 import static org.junit.Assert.*;
 import static org.junit.runners.MethodSorters.*;
@@ -24,7 +25,7 @@ import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.client.*;
 import org.apache.juneau.rest.mock.*;
-import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.http.response.*;
 import org.junit.*;
 
@@ -122,32 +123,28 @@ public class RestOp_Returns_Test {
        @Rest
        public static class B {
                @RestGet
-               public BasicHttpResource a() throws Exception {
-                       return BasicHttpResource.of("foo");
+               public HttpResource a() throws Exception {
+                       return stringResource("foo").build();
                }
                @RestGet
-               public BasicHttpResource b() throws Exception {
-                       return BasicHttpResource.of(null).header("Foo", "Bar");
+               public HttpResource b() throws Exception {
+                       return readerResource(null).header("Foo", 
"Bar").build();
                }
                @RestGet
-               public BasicHttpResource c() throws Exception {
-                       return 
BasicHttpResource.of(null).contentType("application/json");
+               public HttpResource c() throws Exception {
+                       return 
readerResource(null).contentType("application/json").build();
                }
                @RestGet
-               public BasicHttpResource d(RestRequest req) throws Exception {
-                       return 
BasicHttpResource.of(()->req.getVarResolverSession().resolve("$RQ{foo}"));
+               public HttpResource d(RestRequest req) throws Exception {
+                       return 
stringResource(()->req.getVarResolverSession().resolve("$RQ{foo}")).build();
                }
                @RestGet
-               public BasicHttpResource e() throws Exception {
-                       return BasicHttpResource.of(new 
ByteArrayInputStream("foo".getBytes()));
+               public HttpResource e() throws Exception {
+                       return streamResource(new 
ByteArrayInputStream("foo".getBytes())).build();
                }
                @RestGet
-               public BasicHttpResource f() throws Exception {
-                       return BasicHttpResource.of(new StringReader("foo"));
-               }
-               @RestGet
-               public BasicHttpResource g() throws Exception {
-                       return BasicHttpResource.of(new StringBuilder("foo"));
+               public HttpResource f() throws Exception {
+                       return readerResource(new StringReader("foo")).build();
                }
        }
 
@@ -172,9 +169,6 @@ public class RestOp_Returns_Test {
                b.get("/f")
                        .run()
                        .assertBody().is("foo");
-               b.get("/g")
-                       .run()
-                       .assertBody().is("foo");
        }
 
        
//------------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
index 181acbd..fee2687 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_BasicCalls_Test.java
@@ -16,6 +16,7 @@ import static org.apache.juneau.assertions.Assertions.*;
 import static org.junit.runners.MethodSorters.*;
 import static org.apache.juneau.http.HttpParts.*;
 import static org.apache.juneau.http.HttpEntities.*;
+import static org.apache.juneau.http.HttpResources.*;
 
 import java.io.*;
 import java.net.*;
@@ -26,7 +27,6 @@ import org.apache.http.*;
 import org.apache.http.client.utils.*;
 import org.apache.juneau.collections.*;
 import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.part.*;
 import org.apache.juneau.marshall.*;
 import org.apache.juneau.rest.*;
@@ -166,7 +166,7 @@ public class RestClient_BasicCalls_Test {
                List<Object> bodies = AList.<Object>of(
                        new StringReader("{f:1}"),
                        new ByteArrayInputStream("{f:1}".getBytes()),
-                       BasicHttpResource.of("{f:1}"),
+                       stringResource("{f:1}").build(),
                        bean,
                        stringEntity("{f:1}").build(),
                        parts("f",1)
@@ -209,7 +209,7 @@ public class RestClient_BasicCalls_Test {
                List<Object> bodies = AList.<Object>of(
                        new StringReader("{f:1}"),
                        new ByteArrayInputStream("{f:1}".getBytes()),
-                       BasicHttpResource.of("{f:1}"),
+                       stringResource("{f:1}").build(),
                        bean,
                        stringEntity("{f:1}").build(),
                        parts("f",1)
@@ -315,10 +315,10 @@ public class RestClient_BasicCalls_Test {
                        /*[ 3]*/ stringEntity("f=1", 
ContentType.APPLICATION_FORM_URLENCODED).build(),
                        /*[ 4]*/ stringEntity("f=1", null).build(),
                        /*[ 5]*/ part("f","1"),
-                       /*[ 6]*/ BasicHttpResource.of("f=1"),
-                       /*[ 7]*/ BasicHttpResource.of("f=1"),
-                       /*[ 8]*/ 
BasicHttpResource.of("f=1").contentType("application/x-www-form-urlencoded"),
-                       /*[ 9]*/ 
BasicHttpResource.of("f=1").contentType("application/x-www-form-urlencoded"),
+                       /*[ 6]*/ stringResource("f=1").build(),
+                       /*[ 7]*/ stringResource("f=1").build(),
+                       /*[ 8]*/ 
stringResource("f=1").contentType("application/x-www-form-urlencoded").build(),
+                       /*[ 9]*/ 
stringResource("f=1").contentType("application/x-www-form-urlencoded").build(),
                        /*[14]*/ s1,
                        /*[15]*/ s2
                );
@@ -349,7 +349,7 @@ public class RestClient_BasicCalls_Test {
                List<Object> bodies = AList.<Object>of(
                        new StringReader("{f:1}"),
                        new ByteArrayInputStream("{f:1}".getBytes()),
-                       BasicHttpResource.of("{f:1}"),
+                       stringResource("{f:1}").build(),
                        bean,
                        stringEntity("{f:1}").build(),
                        parts("f",1)
@@ -386,7 +386,7 @@ public class RestClient_BasicCalls_Test {
                List<Object> bodies = AList.<Object>of(
                        new StringReader("{f:1}"),
                        new ByteArrayInputStream("{f:1}".getBytes()),
-                       BasicHttpResource.of("{f:1}"),
+                       stringResource("{f:1}").build(),
                        bean,
                        stringEntity("{f:1}").build(),
                        parts("f",1)
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Body_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Body_Test.java
index 80353d0..b89da04 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Body_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Body_Test.java
@@ -14,6 +14,7 @@ package org.apache.juneau.rest.client;
 
 import static org.apache.juneau.http.HttpHeaders.*;
 import static org.apache.juneau.http.HttpEntities.*;
+import static org.apache.juneau.http.HttpResources.*;
 import static org.junit.runners.MethodSorters.*;
 
 import java.io.*;
@@ -21,6 +22,7 @@ import java.util.*;
 
 import org.apache.http.*;
 import org.apache.juneau.http.entity.*;
+import org.apache.juneau.http.resource.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
@@ -48,7 +50,7 @@ public class RestClient_Body_Test {
 
        @Test
        public void a01_BasicHttpResource() throws Exception {
-               BasicHttpResource x1 = httpResource("foo");
+               HttpResource x1 = stringResource("foo").build();
                client().build().post("/", x1).run()
                        .assertHeader("X-Content-Length").asInteger().is(3)
                        .assertHeader("X-Content-Encoding").doesNotExist()
@@ -56,7 +58,7 @@ public class RestClient_Body_Test {
                        .assertHeader("X-Transfer-Encoding").doesNotExist()
                ;
 
-               BasicHttpResource x2 = 
httpResource("foo").contentType("text/plain").contentEncoding("identity");
+               HttpResource x2 = 
stringResource("foo").contentType("text/plain").contentEncoding("identity").build();
                client().build().post("/",x2).run()
                        .assertHeader("X-Content-Length").asInteger().is(3)
                        .assertHeader("X-Content-Encoding").is("identity")
@@ -64,7 +66,7 @@ public class RestClient_Body_Test {
                        .assertHeader("X-Transfer-Encoding").doesNotExist()
                ;
 
-               BasicHttpResource x3 = 
httpResource("foo").contentType(contentType("text/plain")).contentEncoding(contentEncoding("identity")).chunked();
+               HttpResource x3 = 
stringResource("foo").contentType(contentType("text/plain")).contentEncoding(contentEncoding("identity")).chunked().build();
                client().build().post("/",x3).run()
                        .assertHeader("X-Content-Length").doesNotExist()  // 
Missing when chunked.
                        .assertHeader("X-Content-Encoding").is("identity")
@@ -72,7 +74,7 @@ public class RestClient_Body_Test {
                        .assertHeader("X-Transfer-Encoding").is("chunked")
                ;
 
-               BasicHttpResource x4 = new BasicHttpResource("foo", 
contentType("text/plain"), contentEncoding("identity"));
+               HttpResource x4 = stringResource("foo", 
contentType("text/plain")).contentEncoding("identity").build();
                client().build().post("/",x4).run()
                        .assertHeader("X-Content-Length").asInteger().is(3)
                        .assertHeader("X-Content-Encoding").is("identity")
@@ -80,26 +82,26 @@ public class RestClient_Body_Test {
                        .assertHeader("X-Transfer-Encoding").doesNotExist()
                ;
 
-               BasicHttpResource x5 = 
httpResource("foo").header("Foo","bar").header(header("Baz","qux"));
+               HttpResource x5 = 
stringResource("foo").header("Foo","bar").header(header("Baz","qux")).build();
                client().build().post("/",x5).run()
                        .assertHeader("X-Foo").is("bar")
                        .assertHeader("X-Baz").is("qux")
                ;
 
-               BasicHttpResource x6 = 
httpResource("foo").headers(Arrays.asList(header("Foo","bar"),header("Baz","qux")));
+               HttpResource x6 = 
stringResource("foo").headers(Arrays.asList(header("Foo","bar"),header("Baz","qux"))).build();
                client().build().post("/",x6).run()
                        .assertHeader("X-Foo").is("bar")
                        .assertHeader("X-Baz").is("qux")
                ;
 
-               BasicHttpResource x7 = httpResource(new StringReader("foo"));
+               HttpResource x7 = readerResource(new 
StringReader("foo")).build();
                client().build().post("/",x7).run().assertBody().is("foo");
 
-               BasicHttpResource x8 = httpResource(new 
StringReader("foo")).cache();
+               HttpResource x8 = readerResource(new 
StringReader("foo")).cached().build();
                client().build().post("/",x8).run().assertBody().is("foo");
                client().build().post("/",x8).run().assertBody().is("foo");
 
-               BasicHttpResource x9 = httpResource(null);
+               HttpResource x9 = readerResource(null).build();
                client().build().post("/",x9).run().assertBody().isEmpty();
        }
 
@@ -147,7 +149,7 @@ public class RestClient_Body_Test {
                HttpEntity x9 = readerEntity(null).build();
                client().build().post("/",x9).run().assertBody().isEmpty();
 
-               BasicHttpEntity2 x12 = stringEntity("foo").build();
+               BasicHttpEntity x12 = stringEntity("foo").build();
                x12.assertString().is("foo");
                x12.assertBytes().asString().is("foo");
        }
@@ -183,10 +185,6 @@ public class RestClient_Body_Test {
        // Helper methods.
        
//------------------------------------------------------------------------------------------------------------------
 
-       private static BasicHttpResource httpResource(Object val) {
-               return BasicHttpResource.of(val);
-       }
-
        private static RestClientBuilder client() {
                return MockRestClient.create(A.class).simpleJson();
        }

Reply via email to