Repository: incubator-juneau
Updated Branches:
  refs/heads/master 053b7c9ad -> edbba9377


Improvements to @RestResource.stylesheet() and StreamResource.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/edbba937
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/edbba937
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/edbba937

Branch: refs/heads/master
Commit: edbba93773cb2e13aba02d9a5fe6d58efa2f6f14
Parents: 053b7c9
Author: JamesBognar <[email protected]>
Authored: Mon Feb 27 11:14:51 2017 -0500
Committer: JamesBognar <[email protected]>
Committed: Mon Feb 27 11:14:51 2017 -0500

----------------------------------------------------------------------
 .../org/apache/juneau/internal/IOUtils.java     |   5 +-
 juneau-core/src/main/javadoc/overview.html      |   7 +
 .../org/apache/juneau/rest/RestServlet.java     |  23 ++-
 .../org/apache/juneau/rest/StreamResource.java  | 159 ++++++++++++++++---
 .../juneau/rest/annotation/RestResource.java    |   6 +-
 5 files changed, 159 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/edbba937/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java 
b/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
index 254fac9..72ece2c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
@@ -146,10 +146,9 @@ public final class IOUtils {
                try {
                        while ((nRead = in.read(b, 0, b.length)) != -1)
                                  buff.write(b, 0, nRead);
+                       buff.flush();
 
-                               buff.flush();
-
-                               return buff.toByteArray();
+                       return buff.toByteArray();
                } finally {
                        in.close();
                }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/edbba937/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html 
b/juneau-core/src/main/javadoc/overview.html
index d1d3b4a..b7a5b01 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -5668,6 +5668,13 @@
                        </ul>
                </ul>
                
+               <h6 class='topic'>org.apache.juneau.rest</h6>
+               <ul class='spaced-list'>
+                       <li>{@link 
org.apache.juneau.rest.annotation.RestResource#stylesheet()} can now take in a 
comma-delimited list of stylesheet paths.
+                       <li>{@link org.apache.juneau.rest.StreamResource} can 
now contain multiple sources from a variety of source types (e.g. 
<code><jk>byte</jk>[]</code> arrays, <code>InputStreams</code>, 
<code>Files</code>, etc...)
+                               and is now immutable.  It also includes a new 
{@link org.apache.juneau.rest.StreamResource.Builder} class.
+               </ul>
+
                <h6 class='topic'>org.apache.juneau.rest.client</h6>
                <ul class='spaced-list'>
                        <li>{@link 
org.apache.juneau.rest.client.RestClient#setRootUrl(Object)} can now take in 
<code>URI</code> and <code>URL</code> objects.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/edbba937/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
index f351088..9bbd5a4 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -1348,7 +1348,8 @@ public abstract class RestServlet extends HttpServlet {
                                                                int i = 
p2.lastIndexOf('/');
                                                                String name = 
(i == -1 ? p2 : p2.substring(i+1));
                                                                String 
mediaType = getMimetypesFileTypeMap().getContentType(name);
-                                                               
staticFilesCache.put(pathInfo, new StreamResource(is, 
mediaType).setHeader("Cache-Control", "max-age=86400, public"));
+                                                               ObjectMap 
headers = new ObjectMap().append("Cache-Control", "max-age=86400, public");
+                                                               
staticFilesCache.put(pathInfo, new StreamResource(mediaType, headers, is));
                                                                return 
staticFilesCache.get(pathInfo);
                                                        } finally {
                                                                is.close();
@@ -1856,19 +1857,15 @@ public abstract class RestServlet extends HttpServlet {
         * @throws IOException If stylesheet could not be loaded.
         */
        protected StreamResource createStyleSheet() throws IOException {
-               for (RestResource r : 
restResourceAnnotationsChildFirst.values()) {
+               for (RestResource r : 
restResourceAnnotationsChildFirst.values())
                        if (! r.stylesheet().isEmpty()) {
-                               String path = 
getVarResolver().resolve(r.stylesheet());
-                               InputStream is = getResource(path, null);
-                               if (is != null) {
-                                       try {
-                                               return new StreamResource(is, 
"text/css");
-                                       } finally {
-                                               is.close();
-                                       }
-                               }
+                               List<InputStream> contents = new 
ArrayList<InputStream>();
+
+                               for (String path : 
StringUtils.split(getVarResolver().resolve(r.stylesheet()), ','))
+                                       contents.add(getResource(path, null));
+
+                               return new StreamResource("text/css", 
contents.toArray());
                        }
-               }
                return null;
        }
 
@@ -1893,7 +1890,7 @@ public abstract class RestServlet extends HttpServlet {
                                InputStream is = getResource(path, null);
                                if (is != null) {
                                        try {
-                                               return new StreamResource(is, 
"image/x-icon");
+                                               return new 
StreamResource("image/x-icon", is);
                                        } finally {
                                                is.close();
                                        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/edbba937/juneau-rest/src/main/java/org/apache/juneau/rest/StreamResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest/src/main/java/org/apache/juneau/rest/StreamResource.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/StreamResource.java
index c157c44..31290ae 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/StreamResource.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/StreamResource.java
@@ -22,56 +22,166 @@ import org.apache.juneau.rest.response.*;
 /**
  * Represents the contents of a byte stream file with convenience methods for 
adding HTTP response headers.
  * <p>
+ * The purpose of this class is to maintain an in-memory reusable byte array 
of a streamed resource for
+ * the fastest possible streaming.
+ * Therefore, this object is designed to be reused and thread-safe.
+ * <p>
  * This class is handled special by the {@link StreamableHandler} class.
+ * This allows these objects to be returned as responses by REST methods.
  */
 public class StreamResource implements Streamable {
 
-       private byte[] contents;
-       private String mediaType;
-       private Map<String,String> headers = new LinkedHashMap<String,String>();
+       private final byte[][] contents;
+       private final String mediaType;
+       private final Map<String,String> headers;
 
        /**
         * Constructor.
-        * Create a stream resource from a byte array.
         *
-        * @param contents The resource contents.
         * @param mediaType The resource media type.
+        * @param contents The resource contents.
+        * <br>If multiple contents are specified, the results will be 
concatenated.
+        * <br>Contents can be any of the following:
+        * <ul>
+        *      <li><code><jk>byte</jk>[]</code>
+        *      <li><code>InputStream</code>
+        *      <li><code>Reader</code> - Converted to UTF-8 bytes.
+        *      <li><code>File</code>
+        *      <li><code>CharSequence</code> - Converted to UTF-8 bytes.
+        *      </ul>
+        * @throws IOException
         */
-       public StreamResource(byte[] contents, String mediaType) {
-               this.contents = contents;
-               this.mediaType = mediaType;
+       public StreamResource(String mediaType, Object...contents) throws 
IOException {
+               this(mediaType, null, contents);
        }
 
        /**
         * Constructor.
-        * Create a stream resource from an <code>InputStream</code>.
-        * Contents of stream will be loaded into a reusable byte array.
         *
-        * @param contents The resource contents.
         * @param mediaType The resource media type.
+        * @param headers The HTTP response headers for this streamed resource.
+        * @param contents The resource contents.
+        * <br>If multiple contents are specified, the results will be 
concatenated.
+        * <br>Contents can be any of the following:
+        * <ul>
+        *      <li><code><jk>byte</jk>[]</code>
+        *      <li><code>InputStream</code>
+        *      <li><code>Reader</code> - Converted to UTF-8 bytes.
+        *      <li><code>File</code>
+        *      <li><code>CharSequence</code> - Converted to UTF-8 bytes.
+        *      </ul>
         * @throws IOException
         */
-       public StreamResource(InputStream contents, String mediaType) throws 
IOException {
-               this.contents = IOUtils.readBytes(contents, 1024);
+       public StreamResource(String mediaType, Map<String,Object> headers, 
Object...contents) throws IOException {
                this.mediaType = mediaType;
+
+               Map<String,String> m = new LinkedHashMap<String,String>();
+               if (headers != null)
+                       for (Map.Entry<String,Object> e : headers.entrySet())
+                               m.put(e.getKey(), 
StringUtils.toString(e.getValue()));
+               this.headers = Collections.unmodifiableMap(m);
+
+               this.contents = new byte[contents.length][];
+               for (int i = 0; i < contents.length; i++) {
+                       Object c = contents[i];
+                       if (c == null)
+                               this.contents[i] = new byte[0];
+                       else if (c instanceof byte[])
+                               this.contents[i] = (byte[])c;
+                       else if (c instanceof InputStream)
+                               this.contents[i] = 
IOUtils.readBytes((InputStream)c, 1024);
+                       else if (c instanceof File)
+                               this.contents[i] = IOUtils.readBytes((File)c);
+                       else if (c instanceof Reader)
+                               this.contents[i] = 
IOUtils.read((Reader)c).getBytes(IOUtils.UTF8);
+                       else if (c instanceof CharSequence)
+                               this.contents[i] = 
((CharSequence)c).toString().getBytes(IOUtils.UTF8);
+                       else
+                               throw new IOException("Invalid class type 
passed to StreamResource: " + c.getClass().getName());
+               }
        }
 
        /**
-        * Add an HTTP response header.
-        *
-        * @param name The header name.
-        * @param value The header value, converted to a string using {@link 
Object#toString()}.
-        * @return This object (for method chaining).
+        * Builder class for constructing {@link StreamResource} objects.
+        * <p>
         */
-       public StreamResource setHeader(String name, Object value) {
-               headers.put(name, value == null ? "" : value.toString());
-               return this;
+       @SuppressWarnings("hiding")
+       public static class Builder {
+               ArrayList<Object> contents = new ArrayList<Object>();
+               String mediaType;
+               Map<String,String> headers = new LinkedHashMap<String,String>();
+
+               /**
+                * Specifies the resource media type string.
+                * @param mediaType The resource media type string.
+                * @return This object (for method chaining).
+                */
+               public Builder mediaType(String mediaType) {
+                       this.mediaType = mediaType;
+                       return this;
+               }
+
+               /**
+                * Specifies the contents for this resource.
+                * <p>
+                * This method can be called multiple times to add more content.
+                *
+                * @param contents The resource contents.
+                * <br>If multiple contents are specified, the results will be 
concatenated.
+                * <br>Contents can be any of the following:
+                * <ul>
+                *      <li><code><jk>byte</jk>[]</code>
+                *      <li><code>InputStream</code>
+                *      <li><code>Reader</code> - Converted to UTF-8 bytes.
+                *      <li><code>File</code>
+                *      <li><code>CharSequence</code> - Converted to UTF-8 
bytes.
+                *      </ul>
+                * @return This object (for method chaining).
+                */
+               public Builder contents(Object...contents) {
+                       this.contents.addAll(Arrays.asList(contents));
+                       return this;
+               }
+
+               /**
+                * Specifies an HTTP response header value.
+                *
+                * @param name The HTTP header name.
+                * @param value The HTTP header value.  Will be converted to a 
<code>String</code> using {@link Object#toString()}.
+                * @return This object (for method chaining).
+                */
+               public Builder header(String name, Object value) {
+                       this.headers.put(name, StringUtils.toString(value));
+                       return this;
+               }
+
+               /**
+                * Specifies HTTP response header values.
+                *
+                * @param headers The HTTP headers.  Values will be converted 
to <code>Strings</code> using {@link Object#toString()}.
+                * @return This object (for method chaining).
+                */
+               public Builder headers(Map<String,Object> headers) {
+                       for (Map.Entry<String,Object> e : headers.entrySet())
+                               header(e.getKey(), e.getValue());
+                       return this;
+               }
+
+               /**
+                * Create a new {@link StreamResource} using values in this 
builder.
+                *
+                * @return A new immutable {@link StreamResource} object.
+                * @throws IOException
+                */
+               public StreamResource build() throws IOException {
+                       return new StreamResource(mediaType, headers, 
contents.toArray());
+               }
        }
 
+
        /**
         * Get the HTTP response headers.
-        *
-        * @return The HTTP response headers.  Never <jk>null</jk>.
+        * @return The HTTP response headers.  An unmodifiable map.  Never 
<jk>null</jk>.
         */
        public Map<String,String> getHeaders() {
                return headers;
@@ -79,7 +189,8 @@ public class StreamResource implements Streamable {
 
        @Override /* Streamable */
        public void streamTo(OutputStream os) throws IOException {
-               os.write(contents);
+               for (byte[] b : contents)
+                       os.write(b);
        }
 
        @Override /* Streamable */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/edbba937/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
index 1061f9f..3889b84 100644
--- 
a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
+++ 
b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
@@ -511,8 +511,12 @@ public @interface RestResource {
         * <ol>
         *      <li><code>com.foo.mypackage.mystyles</code> package.
         *      <li><code>org.apache.juneau.rest.mystyles</code> package (since 
<code>RestServletDefault</code> is in <code>org.apache.juneau.rest</code>).
-        *      <li><code>[working-dir]/mystyles</code> directory. 
+        *      <li><code>[working-dir]/mystyles</code> directory.
         * </ol>
+        * <p>
+        * Multiple stylesheets can be specified as a comma-delimited list.
+        * When multiple stylesheets are specified, their contents will be 
concatenated and return in the order specified
+        * in the list.
         */
        String stylesheet() default "";
 

Reply via email to