View Javadoc
1   package de.juplo.httpresources;
2   
3   import org.slf4j.Logger;
4   import org.slf4j.LoggerFactory;
5   import org.springframework.core.io.Resource;
6   import org.springframework.core.io.ResourceLoader;
7   
8   import java.util.Arrays;
9   
10  public class HttpResourceChainAwareResourceLoader implements ResourceLoader
11  {
12    private static final Logger LOG =
13        LoggerFactory.getLogger(HttpResourceChainAwareResourceLoader.class);
14  
15  
16    private final ResourceLoader loader;
17    private final String[] sources;
18  
19  
20  
21    public HttpResourceChainAwareResourceLoader(
22        ResourceLoader loader,
23        String... sources)
24    {
25      this.loader = loader;
26      this.sources =
27          Arrays
28              .stream(sources)
29              .map(location -> location.trim())
30              .filter(location ->
31              {
32                if (location.length() > 0)
33                  return true;
34  
35                LOG.warn("Filtered out empty entry from list of sources");
36                return false;
37              })
38              .map(location ->
39              {
40                int length = location.length();
41                if (length > 0 && location.charAt(length - 1) != '/')
42                {
43                  LOG.info("Adding trailing slash to configured source {}", location);
44                  return location + "/";
45                }
46                else
47                  return location;
48              })
49              .toArray(size -> new String[size]);
50    }
51  
52  
53    /**
54     * Returns the first existing resource, or the first, if non of the
55     * resources exists.
56     *
57     * This mimics the behavior of the original resolver as closeley as possible,
58     * asuming, that the first source is a / the local one.
59     *
60     * @param location The name of the requested resource
61     * @return The first existing resource, or the first resource - never <code>null</code>.
62     */
63    @Override
64    public Resource getResource(String location)
65    {
66      // We assure in the constructore, that sources always end with a '/'
67      String relativeResourceName =
68          location.charAt(0) == '/'
69              ? location.substring(1)
70              : location;
71  
72      for (String source : sources)
73      {
74        Resource resource = loader.getResource(source + relativeResourceName);
75  
76        // Return the resource, if it exists.
77        if (resource.exists())
78          return resource;
79      }
80  
81      // If all sources were checked without success, return the resource from
82      // the first source, although it does not exists - the contract of an
83      // {@link ResourceLoader} requires, that the return-value is neve {@code null}.
84      // Therefore, we fall back to the behaviour of our parent-loader.
85      return loader.getResource(location);
86    }
87  
88    @Override
89    public ClassLoader getClassLoader()
90    {
91      return loader.getClassLoader();
92    }
93  }