001/*
002 * Copyright 2012-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.autoconfigure.web;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.List;
022
023import org.springframework.boot.context.properties.ConfigurationProperties;
024import org.springframework.boot.context.properties.NestedConfigurationProperty;
025import org.springframework.context.ResourceLoaderAware;
026import org.springframework.core.io.ClassPathResource;
027import org.springframework.core.io.Resource;
028import org.springframework.core.io.ResourceLoader;
029
030/**
031 * Properties used to configure resource handling.
032 *
033 * @author Phillip Webb
034 * @author Brian Clozel
035 * @author Dave Syer
036 * @author Venil Noronha
037 * @since 1.1.0
038 */
039@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
040public class ResourceProperties implements ResourceLoaderAware {
041
042        private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };
043
044        private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
045                        "classpath:/META-INF/resources/", "classpath:/resources/",
046                        "classpath:/static/", "classpath:/public/" };
047
048        private static final String[] RESOURCE_LOCATIONS;
049
050        static {
051                RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
052                                + SERVLET_RESOURCE_LOCATIONS.length];
053                System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
054                                SERVLET_RESOURCE_LOCATIONS.length);
055                System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
056                                SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
057        }
058
059        /**
060         * Locations of static resources. Defaults to classpath:[/META-INF/resources/,
061         * /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
062         */
063        private String[] staticLocations = RESOURCE_LOCATIONS;
064
065        /**
066         * Cache period for the resources served by the resource handler, in seconds.
067         */
068        private Integer cachePeriod;
069
070        /**
071         * Enable default resource handling.
072         */
073        private boolean addMappings = true;
074
075        private final Chain chain = new Chain();
076
077        private ResourceLoader resourceLoader;
078
079        @Override
080        public void setResourceLoader(ResourceLoader resourceLoader) {
081                this.resourceLoader = resourceLoader;
082        }
083
084        public String[] getStaticLocations() {
085                return this.staticLocations;
086        }
087
088        public void setStaticLocations(String[] staticLocations) {
089                this.staticLocations = appendSlashIfNecessary(staticLocations);
090        }
091
092        private String[] appendSlashIfNecessary(String[] staticLocations) {
093                String[] normalized = new String[staticLocations.length];
094                for (int i = 0; i < staticLocations.length; i++) {
095                        String location = staticLocations[i];
096                        normalized[i] = (location.endsWith("/") ? location : location + "/");
097                }
098                return normalized;
099        }
100
101        public Resource getWelcomePage() {
102                for (String location : getStaticWelcomePageLocations()) {
103                        Resource resource = this.resourceLoader.getResource(location);
104                        try {
105                                if (resource.exists()) {
106                                        resource.getURL();
107                                        return resource;
108                                }
109                        }
110                        catch (Exception ex) {
111                                // Ignore
112                        }
113                }
114                return null;
115        }
116
117        private String[] getStaticWelcomePageLocations() {
118                String[] result = new String[this.staticLocations.length];
119                for (int i = 0; i < result.length; i++) {
120                        String location = this.staticLocations[i];
121                        if (!location.endsWith("/")) {
122                                location = location + "/";
123                        }
124                        result[i] = location + "index.html";
125                }
126                return result;
127        }
128
129        List<Resource> getFaviconLocations() {
130                List<Resource> locations = new ArrayList<Resource>(
131                                this.staticLocations.length + 1);
132                if (this.resourceLoader != null) {
133                        for (String location : this.staticLocations) {
134                                locations.add(this.resourceLoader.getResource(location));
135                        }
136                }
137                locations.add(new ClassPathResource("/"));
138                return Collections.unmodifiableList(locations);
139        }
140
141        public Integer getCachePeriod() {
142                return this.cachePeriod;
143        }
144
145        public void setCachePeriod(Integer cachePeriod) {
146                this.cachePeriod = cachePeriod;
147        }
148
149        public boolean isAddMappings() {
150                return this.addMappings;
151        }
152
153        public void setAddMappings(boolean addMappings) {
154                this.addMappings = addMappings;
155        }
156
157        public Chain getChain() {
158                return this.chain;
159        }
160
161        /**
162         * Configuration for the Spring Resource Handling chain.
163         */
164        public static class Chain {
165
166                /**
167                 * Enable the Spring Resource Handling chain. Disabled by default unless at least
168                 * one strategy has been enabled.
169                 */
170                private Boolean enabled;
171
172                /**
173                 * Enable caching in the Resource chain.
174                 */
175                private boolean cache = true;
176
177                /**
178                 * Enable HTML5 application cache manifest rewriting.
179                 */
180                private boolean htmlApplicationCache = false;
181
182                /**
183                 * Enable resolution of already gzipped resources. Checks for a resource name
184                 * variant with the "*.gz" extension.
185                 */
186                private boolean gzipped = false;
187
188                @NestedConfigurationProperty
189                private final Strategy strategy = new Strategy();
190
191                /**
192                 * Return whether the resource chain is enabled. Return {@code null} if no
193                 * specific settings are present.
194                 * @return whether the resource chain is enabled or {@code null} if no specified
195                 * settings are present.
196                 */
197                public Boolean getEnabled() {
198                        return getEnabled(getStrategy().getFixed().isEnabled(),
199                                        getStrategy().getContent().isEnabled(), this.enabled);
200                }
201
202                public void setEnabled(boolean enabled) {
203                        this.enabled = enabled;
204                }
205
206                public boolean isCache() {
207                        return this.cache;
208                }
209
210                public void setCache(boolean cache) {
211                        this.cache = cache;
212                }
213
214                public Strategy getStrategy() {
215                        return this.strategy;
216                }
217
218                public boolean isHtmlApplicationCache() {
219                        return this.htmlApplicationCache;
220                }
221
222                public void setHtmlApplicationCache(boolean htmlApplicationCache) {
223                        this.htmlApplicationCache = htmlApplicationCache;
224                }
225
226                public boolean isGzipped() {
227                        return this.gzipped;
228                }
229
230                public void setGzipped(boolean gzipped) {
231                        this.gzipped = gzipped;
232                }
233
234                static Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled,
235                                Boolean chainEnabled) {
236                        return (fixedEnabled || contentEnabled ? Boolean.TRUE : chainEnabled);
237                }
238
239        }
240
241        /**
242         * Strategies for extracting and embedding a resource version in its URL path.
243         */
244        public static class Strategy {
245
246                @NestedConfigurationProperty
247                private final Fixed fixed = new Fixed();
248
249                @NestedConfigurationProperty
250                private final Content content = new Content();
251
252                public Fixed getFixed() {
253                        return this.fixed;
254                }
255
256                public Content getContent() {
257                        return this.content;
258                }
259
260        }
261
262        /**
263         * Version Strategy based on content hashing.
264         */
265        public static class Content {
266
267                /**
268                 * Enable the content Version Strategy.
269                 */
270                private boolean enabled;
271
272                /**
273                 * Comma-separated list of patterns to apply to the Version Strategy.
274                 */
275                private String[] paths = new String[] { "/**" };
276
277                public boolean isEnabled() {
278                        return this.enabled;
279                }
280
281                public void setEnabled(boolean enabled) {
282                        this.enabled = enabled;
283                }
284
285                public String[] getPaths() {
286                        return this.paths;
287                }
288
289                public void setPaths(String[] paths) {
290                        this.paths = paths;
291                }
292
293        }
294
295        /**
296         * Version Strategy based on a fixed version string.
297         */
298        public static class Fixed {
299
300                /**
301                 * Enable the fixed Version Strategy.
302                 */
303                private boolean enabled;
304
305                /**
306                 * Comma-separated list of patterns to apply to the Version Strategy.
307                 */
308                private String[] paths = new String[] { "/**" };
309
310                /**
311                 * Version string to use for the Version Strategy.
312                 */
313                private String version;
314
315                public boolean isEnabled() {
316                        return this.enabled;
317                }
318
319                public void setEnabled(boolean enabled) {
320                        this.enabled = enabled;
321                }
322
323                public String[] getPaths() {
324                        return this.paths;
325                }
326
327                public void setPaths(String[] paths) {
328                        this.paths = paths;
329                }
330
331                public String getVersion() {
332                        return this.version;
333                }
334
335                public void setVersion(String version) {
336                        this.version = version;
337                }
338
339        }
340
341}