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 9882259  JUNEAU-142, JUNEAU-143
9882259 is described below

commit 98822599ea5ca9bd267b125681997b1076c4675b
Author: JamesBognar <[email protected]>
AuthorDate: Sun Sep 8 11:28:09 2019 -0400

    JUNEAU-142, JUNEAU-143
    
    Static files mapping doesn't properly handle override scenarios.
    Add support for absolute paths on @RestResource(staticFiles)
---
 .../org/apache/juneau/PropertyStoreBuilder.java    |  6 +-
 .../utils/ClasspathResourceFinderSimple.java       | 15 +---
 juneau-doc/docs/ReleaseNotes/8.1.1.html            |  9 +++
 .../07.juneau-rest-server/26.StaticFiles.html      |  2 +-
 juneau-doc/docs/docs.txt                           |  1 +
 .../annotation/RestResourceStaticFilesTest.java    | 48 +++++++++++-
 .../java/org/apache/juneau/rest/RestContext.java   | 87 +++++++++++++++-------
 .../org/apache/juneau/rest/StaticFileMapping.java  | 36 +++++++--
 .../java/org/apache/juneau/rest/StaticFiles.java   | 76 +++++++++++++++++++
 9 files changed, 232 insertions(+), 48 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
index 55d9f02..05fd053 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
@@ -341,6 +341,7 @@ public class PropertyStoreBuilder {
         *              Out-of-range indexes are simply 'adjusted' to the 
beginning or the end of the list.
         *              So, for example, a value of <js>"-100"</js> will always 
just cause the entry to be added to the beginning
         *              of the list.
+        *              <br>NOTE:  If <jk>null</jk>, value will be inserted at 
position 0.
         *      <br>For MAPs, this can be <jk>null</jk> if we're adding a map, 
or a string key if we're adding an entry.
         * @param value
         *      The new value to add to the property.
@@ -370,11 +371,14 @@ public class PropertyStoreBuilder {
        }
 
        /**
-        * Adds a value to a SET, LIST, or MAP property.
+        * Adds/prepends a value to a SET, LIST, or MAP property.
         *
         * <p>
         * Shortcut for calling <code>addTo(key, <jk>null</jk>, value);</code>.
         *
+        * <p>
+        * NOTE:  When adding to a list, the value is inserted at the beginning 
of the list.
+        *
         * @param key The property key.
         * @param value
         *      The new value to add to the property.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
index 7eea5f3..eb97c8f 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
@@ -65,10 +65,10 @@ public class ClasspathResourceFinderSimple implements 
ClasspathResourceFinder {
         * @throws IOException Thrown by underlying stream.
         */
        protected InputStream findClasspathResource(Class<?> baseClass, String 
name, Locale locale) throws IOException {
-               
-               if (locale == null) 
+
+               if (locale == null)
                        return getResourceAsStream(baseClass, name);
-               
+
                for (String n : getCandidateFileNames(name, locale)) {
                        InputStream is = getResourceAsStream(baseClass, n);
                        if (is != null)
@@ -78,14 +78,7 @@ public class ClasspathResourceFinderSimple implements 
ClasspathResourceFinder {
        }
 
        private InputStream getResourceAsStream(Class<?> baseClass, String 
name) {
-               InputStream is = baseClass.getResourceAsStream(name);
-               if (is != null)
-                       return is;
-               if (! name.startsWith("/"))
-                       is = baseClass.getResourceAsStream("/" + name);
-               if (is != null)
-                       return is;
-               return null;
+               return baseClass.getResourceAsStream(name);
        }
 
        /**
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.1.html 
b/juneau-doc/docs/ReleaseNotes/8.1.1.html
index fe2213d..ca60e8f 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.1.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.1.html
@@ -74,6 +74,15 @@
                                <li class='jm'>{@link 
oajr.BasicRest#log(Level,Throwable,String,Object[]) 
log(Level,Throwable,String,Object[])}
                        </ul>
                </ul>
+       <li>
+               The <c>@RestResource(staticFiles)</c> annotation now supports 
absolute path locations:
+               <p class='bpcode w800'>
+       <jc>// Resolves static files in root package "htdocs" or working 
directory "htdocs".
+       <ja>@RestResource</ja>(staticFiles=<js>"htdocs:/htdocs"</js>)
+               </p>
+       <li>
+               Fixed a bug in <c>@RestResource(staticFiles)</c> where the 
order of lookup between parent and child resources
+               was wrong.
 </ul>
 
 <h5 class='topic w800'>juneau-rest-client</h5>
diff --git a/juneau-doc/docs/Topics/07.juneau-rest-server/26.StaticFiles.html 
b/juneau-doc/docs/Topics/07.juneau-rest-server/26.StaticFiles.html
index bf55663..dcf82d7 100644
--- a/juneau-doc/docs/Topics/07.juneau-rest-server/26.StaticFiles.html
+++ b/juneau-doc/docs/Topics/07.juneau-rest-server/26.StaticFiles.html
@@ -13,7 +13,7 @@
  
***************************************************************************************************************************/
  -->
 
-Static files
+{todo} Static files
 
 <p>
        The {@link oajr.annotation.RestResource#staticFiles 
@RestResource(staticFiles)} 
diff --git a/juneau-doc/docs/docs.txt b/juneau-doc/docs/docs.txt
index 56d6f0b..4563d0f 100644
--- a/juneau-doc/docs/docs.txt
+++ b/juneau-doc/docs/docs.txt
@@ -4,6 +4,7 @@ PojoCategories = #juneau-marshall.PojoCategories, POJO 
Categories
 PojosConveribleToStrings = #PojosConveribleToStrings, POJOs Convertible 
to/from Strings
 PojosConveribleToOtherTypes = #PojosConveribleToOtherTypes, POJOs Convertible 
to/from Other Types
 ConfigurableProperties = #juneau-marshall.ConfigurableProperties, Configurable 
Properties
+SimpleJson = #juneau-marshall.JsonDetails.SimplifiedJson, Simple JSON
 
 SwaggerIO.v2 = https://swagger.io/specification/v2
 SwaggerIO.v3 = https://swagger.io/specification
diff --git 
a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestResourceStaticFilesTest.java
 
b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestResourceStaticFilesTest.java
index 2a5b5e3..f9817b3 100644
--- 
a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestResourceStaticFilesTest.java
+++ 
b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestResourceStaticFilesTest.java
@@ -23,9 +23,9 @@ import org.junit.runners.*;
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class RestResourceStaticFilesTest {
 
-       
//====================================================================================================
+       
//------------------------------------------------------------------------------------------------------------------
        // Basic tests
-       
//====================================================================================================
+       
//------------------------------------------------------------------------------------------------------------------
 
        @RestResource(staticFiles={"xdocs:xdocs","xdocs2:xdocs2:{Foo:'Bar'}"})
        public static class A {
@@ -47,9 +47,9 @@ public class RestResourceStaticFilesTest {
                
a.get("/xdocs/xsubdocs/%2E%2E/test.txt?noTrace=true").execute().assertStatus(404);
        }
 
-       
//====================================================================================================
+       
//------------------------------------------------------------------------------------------------------------------
        // Static files with response headers.
-       
//====================================================================================================
+       
//------------------------------------------------------------------------------------------------------------------
 
        @RestResource(staticFiles={"xdocs:xdocs:{Foo:'Bar'}"})
        public static class B {
@@ -64,4 +64,44 @@ public class RestResourceStaticFilesTest {
        public void b01() throws Exception {
                
b.get("/xdocs/test.txt").execute().assertHeader("Foo","Bar").assertBodyContains("OK-1");
        }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Class hierarchy
+       
//------------------------------------------------------------------------------------------------------------------
+
+       @RestResource(staticFiles={"xdocs:xdocs"})
+       public static class C1 {
+               @RestMethod
+               public String c01() {
+                       return null;
+               }
+       }
+
+       @RestResource(staticFiles={"xdocs:/xdocs"})
+       public static class C2 extends C1 {
+               @RestMethod
+               public String c02() {
+                       return null;
+               }
+       }
+
+       static MockRest c1 = MockRest.build(C1.class);
+       static MockRest c2 = MockRest.build(C2.class);
+
+       @Test
+       public void c01() throws Exception {
+               // Should resolve to relative xdocs folder.
+               c1.get("/xdocs/test.txt").execute().assertBodyContains("OK-1");
+               
c1.get("/xdocs/xsubdocs/test.txt").execute().assertBodyContains("OK-2");
+
+               // Should be overridden to absolute xdocs folder.
+               c2.get("/xdocs/test.txt").execute().assertBodyContains("OK-3");
+               
c2.get("/xdocs/xsubdocs/test.txt").execute().assertBodyContains("OK-4");
+
+               // Should pick up from file system.
+               c1.get("/xdocs/test2.txt").execute().assertBodyContains("OK-5");
+               c2.get("/xdocs/test2.txt").execute().assertBodyContains("OK-5");
+               
c1.get("/xdocs/xsubdocs/test2.txt").execute().assertBodyContains("OK-6");
+               
c2.get("/xdocs/xsubdocs/test2.txt").execute().assertBodyContains("OK-6");
+       }
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 7291510..15b3fd7 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -50,7 +50,6 @@ import org.apache.juneau.http.annotation.Query;
 import org.apache.juneau.http.annotation.Response;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.httppart.bean.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.jsonschema.*;
 import org.apache.juneau.msgpack.*;
@@ -2755,6 +2754,16 @@ public final class RestContext extends BeanContext {
         *      }
         * </p>
         *
+        * <p>
+        * Note that headers can also be specified per path-mapping via the 
{@link RestResource#staticFiles() @RestResource(staticFiles)} annotation.
+        * <p class='bcode w800'>
+        *      <ja>@RestResource</ja>(
+        *              staticFiles={
+        *                      
<js>"htdocs:docs:{'Cache-Control':'max-age=86400, public'}"</js>
+        *              }
+        *      )
+        * </p>
+        *
         * <ul class='seealso'>
         *      <li class='jf'>{@link #REST_staticFiles} for information about 
statically-served files.
         * </ul>
@@ -2790,6 +2799,13 @@ public final class RestContext extends BeanContext {
         * from the classpath or file system.
         *
         * <p>
+        * The format of the value is one of the following:
+        * <ol class='spaced-list'>
+        *      <li><js>"path:location"</js>
+        *      <li><js>"path:location:headers"</js>
+        * </ol>
+        *
+        * <p>
         * An example where this class is used is in the {@link 
RestResource#staticFiles} annotation:
         * <p class='bcode w800'>
         *      <jk>package</jk> com.foo.mypackage;
@@ -2809,12 +2825,41 @@ public final class RestContext extends BeanContext {
         * <p class='bcode w800'>
         *      /myresource/htdocs/foobar.html
         * </p>
-        * <br>...the servlet will attempt to find the <c>foobar.html</c> file 
in the following ordered locations:
+        * <br>...the servlet will attempt to find the <c>foobar.html</c> file 
in the following location:
         * <ol class='spaced-list'>
         *      <li><c>com.foo.mypackage.docs</c> package.
-        *      <li><c>[working-dir]/docs</c> directory.
         * </ol>
         *
+        * <p>
+        * The location is interpreted as an absolute path if it starts with 
<js>'/'</js>.
+        * <p class='bcode w800'>
+        *      <ja>@RestResource</ja>(
+        *              staticFiles={
+        *                      <js>"htdocs:/docs"</js>
+        *              }
+        *      )
+        * </p>
+        * <p>
+        * In the example above, given a GET request to the following URL...
+        * <p class='bcode w800'>
+        *      /myresource/htdocs/foobar.html
+        * </p>
+        * <br>...the servlet will attempt to find the <c>foobar.html</c> file 
in the following location:
+        * <ol class='spaced-list'>
+        *      <li><c>docs</c> package (typically under 
<c>src/main/resources/docs</c> in your workspace).
+        *      <li><c>[working-dir]/docs</c> directory at runtime.
+        * </ol>
+        *
+        * <p>
+        * Response headers can be specified for served files by adding a 3rd 
section that consists of a {@doc SimpleJson} object.
+        * <p class='bcode w800'>
+        *      <ja>@RestResource</ja>(
+        *              staticFiles={
+        *                      
<js>"htdocs:docs:{'Cache-Control':'max-age=86400, public'}"</js>
+        *              }
+        *      )
+        * </p>
+        *
         * <ul class='seealso'>
         *      <li class='jf'>{@link #REST_classpathResourceFinder} for 
configuring how classpath resources are located and retrieved.
         *      <li class='jf'>{@link #REST_mimeTypes} for configuring the 
media types based on file extension.
@@ -2828,6 +2873,8 @@ public final class RestContext extends BeanContext {
         *              Mappings are cumulative from super classes.
         *      <li>
         *              Child resources can override mappings made on parent 
class resources.
+        *              <br>When both parent and child resources map against 
the same path, files will be search in the child location
+        *              and then the parent location.
         * </ul>
         */
        public static final String REST_staticFiles = PREFIX + 
".staticFiles.lo";
@@ -3485,7 +3532,7 @@ public final class RestContext extends BeanContext {
        private final ObjectMap defaultRequestAttributes;
        private final ResponseHandler[] responseHandlers;
        private final MimetypesFileTypeMap mimetypesFileTypeMap;
-       private final StaticFileMapping[] staticFiles;
+       private final StaticFiles[] staticFiles;
        private final String[] staticFilesPaths;
        private final MessageBundle msgs;
        private final Config config;
@@ -3688,10 +3735,14 @@ public final class RestContext extends BeanContext {
                        consumes = getListProperty(REST_consumes, 
MediaType.class, parsers.getSupportedMediaTypes());
                        produces = getListProperty(REST_produces, 
MediaType.class, serializers.getSupportedMediaTypes());
 
-                       staticFiles = 
ArrayUtils.reverse(getArrayProperty(REST_staticFiles, StaticFileMapping.class));
+                       StaticFileMapping[] staticFileMappings = 
getArrayProperty(REST_staticFiles, StaticFileMapping.class, new 
StaticFileMapping[0]);
+                       staticFiles = new 
StaticFiles[staticFileMappings.length];
+                       for (int i = 0; i < staticFiles.length; i++)
+                               staticFiles[i] = new 
StaticFiles(staticFileMappings[i], staticResourceManager, mimetypesFileTypeMap, 
staticFileResponseHeaders);
+
                        Set<String> s = new TreeSet<>();
-                       for (StaticFileMapping sfm : staticFiles)
-                               s.add(sfm.path);
+                       for (StaticFiles sf : staticFiles)
+                               s.add(sf.getPath());
                        staticFilesPaths = s.toArray(new String[s.size()]);
 
                        MessageBundleLocation[] mbl = 
getInstanceArrayProperty(REST_messages, MessageBundleLocation.class, new 
MessageBundleLocation[0]);
@@ -4085,24 +4136,10 @@ public final class RestContext extends BeanContext {
                        if (p.indexOf("..") != -1)
                                throw new NotFound("Invalid path");
                        StreamResource sr = null;
-                       for (StaticFileMapping sfm : staticFiles) {
-                               String path = sfm.path;
-                               if (p.startsWith(path)) {
-                                       String remainder = (p.equals(path) ? "" 
: p.substring(path.length()));
-                                       if (remainder.isEmpty() || 
remainder.startsWith("/")) {
-                                               String p2 = sfm.location + 
remainder;
-                                               try (InputStream is = 
getClasspathResource(sfm.resourceClass, p2, null)) {
-                                                       if (is != null) {
-                                                               int i = 
p2.lastIndexOf('/');
-                                                               String name = 
(i == -1 ? p2 : p2.substring(i+1));
-                                                               String 
mediaType = mimetypesFileTypeMap.getContentType(name);
-                                                               
Map<String,Object> responseHeaders = sfm.responseHeaders != null ? 
sfm.responseHeaders : staticFileResponseHeaders;
-                                                               sr = new 
StreamResource(MediaType.forString(mediaType), responseHeaders, true, is);
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                               }
+                       for (StaticFiles sf : staticFiles) {
+                               sr = sf.resolve(p);
+                               if (sr != null)
+                                       break;
                        }
                        StaticFile sf = new StaticFile(sr);
                        if (useClasspathResourceCaching) {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
index 2bdec39..3fa0424 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
@@ -13,11 +13,12 @@
 package org.apache.juneau.rest;
 
 import static org.apache.juneau.internal.CollectionUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
 
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.utils.*;
@@ -86,8 +87,8 @@ public class StaticFileMapping {
         */
        public StaticFileMapping(Class<?> resourceClass, String path, String 
location, Map<String,Object> responseHeaders) {
                this.resourceClass = resourceClass;
-               this.path = StringUtils.trimSlashes(path);
-               this.location = StringUtils.trimSlashes(location);
+               this.path = trimSlashes(path);
+               this.location = trimTrailingSlashes(location);
                this.responseHeaders = immutableMap(responseHeaders);
        }
 
@@ -109,11 +110,11 @@ public class StaticFileMapping {
         */
        public StaticFileMapping(Class<?> resourceClass, String mappingString) {
                this.resourceClass = resourceClass;
-               String[] parts = StringUtils.split(mappingString, ':', 3);
+               String[] parts = split(mappingString, ':', 3);
                if (parts == null || parts.length <= 1)
                        throw new FormattedRuntimeException("Invalid mapping 
string format: ''{0}'' on resource class ''{1}''", mappingString, 
resourceClass.getName());
-               this.path = StringUtils.trimSlashes(parts[0]);
-               this.location = StringUtils.trimSlashes(parts[1]);
+               this.path = trimSlashes(parts[0]);
+               this.location = trimTrailingSlashes(parts[1]);
                if (parts.length == 3) {
                        try {
                                responseHeaders = unmodifiableMap(new 
ObjectMap(parts[2]));
@@ -124,4 +125,27 @@ public class StaticFileMapping {
                        responseHeaders = null;
                }
        }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Other methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Override /* Object */
+       public String toString() {
+               return SimpleJsonSerializer.DEFAULT_READABLE.toString(toMap());
+       }
+
+       /**
+        * Returns the properties defined on this bean as a simple map for 
debugging purposes.
+        *
+        * @return A new map containing the properties defined on this bean.
+        */
+       public ObjectMap toMap() {
+               return new DefaultFilteringObjectMap()
+                       .append("resourceClass", resourceClass)
+                       .append("path", path)
+                       .append("location", location)
+                       .append("responseHeaders", responseHeaders)
+               ;
+       }
 }
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
new file mode 100644
index 0000000..e7d859d
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFiles.java
@@ -0,0 +1,76 @@
+// 
***************************************************************************************************************************
+// * 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.                                              *
+// 
***************************************************************************************************************************
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import java.io.*;
+import java.util.*;
+
+import javax.activation.*;
+
+import org.apache.juneau.http.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * The static file resource resolver for a single {@link StaticFileMapping}.
+ */
+class StaticFiles {
+       private final Class<?> resourceClass;
+       private final String path, location;
+       private final Map<String,Object> responseHeaders;
+
+       private final ClasspathResourceManager staticResourceManager;
+       private final MimetypesFileTypeMap mimetypesFileTypeMap;
+
+       StaticFiles(StaticFileMapping sfm, ClasspathResourceManager 
staticResourceManager, MimetypesFileTypeMap mimetypesFileTypeMap, 
Map<String,Object> staticFileResponseHeaders) {
+               this.resourceClass = sfm.resourceClass;
+               this.path = sfm.path;
+               this.location = sfm.location;
+               this.responseHeaders = sfm.responseHeaders != null ? 
sfm.responseHeaders : staticFileResponseHeaders;
+               this.staticResourceManager = staticResourceManager;
+               this.mimetypesFileTypeMap = mimetypesFileTypeMap;
+       }
+
+       String getPath() {
+               return path;
+       }
+
+       StreamResource resolve(String p) throws IOException {
+               if (p.startsWith(path)) {
+                       String remainder = (p.equals(path) ? "" : 
p.substring(path.length()));
+                       if (remainder.isEmpty() || remainder.startsWith("/")) {
+                               String p2 = location + remainder;
+                               try (InputStream is = 
staticResourceManager.getStream(resourceClass, p2, null)) {
+                                       if (is != null) {
+                                               int i = p2.lastIndexOf('/');
+                                               String name = (i == -1 ? p2 : 
p2.substring(i+1));
+                                               String mediaType = 
mimetypesFileTypeMap.getContentType(name);
+                                               return new 
StreamResource(MediaType.forString(mediaType), responseHeaders, true, is);
+                                       }
+                               }
+                       }
+               }
+               return null;
+       }
+}

Reply via email to