Skip to content

Commit 2a309a0

Browse files
committed
Revise charset by location support for static resources
1 parent 03fef65 commit 2a309a0

File tree

8 files changed

+108
-141
lines changed

8 files changed

+108
-141
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceUtils.java

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,14 @@
1616

1717
package org.springframework.web.servlet.config;
1818

19-
import java.nio.charset.Charset;
2019
import java.util.LinkedHashMap;
21-
import java.util.List;
2220
import java.util.Map;
2321

2422
import org.springframework.beans.factory.config.BeanDefinition;
2523
import org.springframework.beans.factory.config.RuntimeBeanReference;
2624
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
2725
import org.springframework.beans.factory.support.RootBeanDefinition;
2826
import org.springframework.beans.factory.xml.ParserContext;
29-
import org.springframework.core.io.Resource;
30-
import org.springframework.core.io.ResourceLoader;
31-
import org.springframework.core.io.UrlResource;
3227
import org.springframework.util.AntPathMatcher;
3328
import org.springframework.util.PathMatcher;
3429
import org.springframework.web.cors.CorsConfiguration;
@@ -64,8 +59,6 @@ public abstract class MvcNamespaceUtils {
6459

6560
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
6661

67-
private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset=";
68-
6962

7063
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
7164
registerBeanNameUrlHandlerMapping(parserContext, source);
@@ -228,40 +221,4 @@ public static Object getContentNegotiationManager(ParserContext context) {
228221
return null;
229222
}
230223

231-
/**
232-
* Load the {@link Resource}'s for the given locations with the given
233-
* {@link ResourceLoader} and add them to the output list. Also for
234-
* {@link org.springframework.core.io.UrlResource URL-based resources} (e.g. files,
235-
* HTTP URLs, etc) this method supports a special prefix to indicate the charset
236-
* associated with the URL so that relative paths appended to it can be encoded
237-
* correctly, e.g. {@code [charset=Windows-31J]http://example.org/path}.
238-
* The charsets, if any, are added to the output map.
239-
* @since 4.3.13
240-
*/
241-
public static void loadResourceLocations(String[] locations, ResourceLoader resourceLoader,
242-
List<Resource> outputLocations, Map<Resource, Charset> outputLocationCharsets) {
243-
244-
for (String location : locations) {
245-
Charset charset = null;
246-
location = location.trim();
247-
if (location.startsWith(URL_RESOURCE_CHARSET_PREFIX)) {
248-
int endIndex = location.indexOf("]", URL_RESOURCE_CHARSET_PREFIX.length());
249-
if (endIndex == -1) {
250-
throw new IllegalArgumentException("Invalid charset syntax in location: " + location);
251-
}
252-
String value = location.substring(URL_RESOURCE_CHARSET_PREFIX.length(), endIndex);
253-
charset = Charset.forName(value);
254-
location = location.substring(endIndex + 1);
255-
}
256-
Resource resource = resourceLoader.getResource(location);
257-
outputLocations.add(resource);
258-
if (charset != null) {
259-
if (!(resource instanceof UrlResource)) {
260-
throw new IllegalArgumentException("Unexpected charset for non-UrlResource: " + resource);
261-
}
262-
outputLocationCharsets.put(resource, charset);
263-
}
264-
}
265-
}
266-
267224
}

spring-webmvc/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,13 @@
1616

1717
package org.springframework.web.servlet.config;
1818

19-
import java.nio.charset.Charset;
20-
import java.util.ArrayList;
21-
import java.util.Arrays;
22-
import java.util.HashMap;
23-
import java.util.List;
2419
import java.util.Map;
2520
import java.util.concurrent.TimeUnit;
2621

2722
import org.w3c.dom.Element;
2823

2924
import org.springframework.beans.MutablePropertyValues;
3025
import org.springframework.beans.factory.config.BeanDefinition;
31-
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
3226
import org.springframework.beans.factory.config.ConstructorArgumentValues;
3327
import org.springframework.beans.factory.config.RuntimeBeanReference;
3428
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
@@ -39,8 +33,6 @@
3933
import org.springframework.beans.factory.xml.ParserContext;
4034
import org.springframework.cache.concurrent.ConcurrentMapCache;
4135
import org.springframework.core.Ordered;
42-
import org.springframework.core.io.Resource;
43-
import org.springframework.core.io.ResourceLoader;
4436
import org.springframework.http.CacheControl;
4537
import org.springframework.util.ClassUtils;
4638
import org.springframework.util.StringUtils;
@@ -170,35 +162,13 @@ private String registerResourceHandler(
170162
return null;
171163
}
172164

173-
String[] locationValues = StringUtils.commaDelimitedListToStringArray(locationAttr);
174-
if (context.getRegistry() instanceof ConfigurableBeanFactory) {
175-
ConfigurableBeanFactory cbf = ((ConfigurableBeanFactory) context.getRegistry());
176-
for (int i = 0; i < locationValues.length; i++) {
177-
locationValues[i] = cbf.resolveEmbeddedValue(locationValues[i]);
178-
}
179-
}
180-
181-
ManagedList<Object> locations = new ManagedList<Object>();
182-
Map<Resource, Charset> locationCharsets = new HashMap<Resource, Charset>();
183-
ResourceLoader resourceLoader = context.getReaderContext().getResourceLoader();
184-
185-
if (resourceLoader != null) {
186-
List<Resource> resources = new ArrayList<Resource>();
187-
MvcNamespaceUtils.loadResourceLocations(locationValues, resourceLoader, resources, locationCharsets);
188-
locations.addAll(resources);
189-
}
190-
else {
191-
locations.addAll(Arrays.asList(locationValues));
192-
}
193-
194165
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
195166
resourceHandlerDef.setSource(source);
196167
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
197168

198169
MutablePropertyValues values = resourceHandlerDef.getPropertyValues();
199170
values.add("urlPathHelper", pathHelperRef);
200-
values.add("locations", locations);
201-
values.add("locationCharsets", locationCharsets);
171+
values.add("locationValues", StringUtils.commaDelimitedListToStringArray(locationAttr));
202172

203173
String cacheSeconds = element.getAttribute("cache-period");
204174
if (StringUtils.hasText(cacheSeconds)) {

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,13 @@
1616

1717
package org.springframework.web.servlet.config.annotation;
1818

19-
import java.nio.charset.Charset;
2019
import java.util.ArrayList;
21-
import java.util.HashMap;
20+
import java.util.Arrays;
2221
import java.util.List;
23-
import java.util.Map;
2422

2523
import org.springframework.cache.Cache;
26-
import org.springframework.core.io.Resource;
27-
import org.springframework.core.io.ResourceLoader;
2824
import org.springframework.http.CacheControl;
2925
import org.springframework.util.Assert;
30-
import org.springframework.web.servlet.config.MvcNamespaceUtils;
3126
import org.springframework.web.servlet.resource.PathResourceResolver;
3227
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
3328

@@ -41,13 +36,9 @@
4136
*/
4237
public class ResourceHandlerRegistration {
4338

44-
private final ResourceLoader resourceLoader;
45-
4639
private final String[] pathPatterns;
4740

48-
private final List<Resource> locations = new ArrayList<Resource>();
49-
50-
private final Map<Resource, Charset> locationCharsets = new HashMap<Resource, Charset>();
41+
private final List<String> locationValues = new ArrayList<String>(4);
5142

5243
private Integer cachePeriod;
5344

@@ -58,12 +49,10 @@ public class ResourceHandlerRegistration {
5849

5950
/**
6051
* Create a {@link ResourceHandlerRegistration} instance.
61-
* @param resourceLoader a resource loader for turning a String location into a {@link Resource}
6252
* @param pathPatterns one or more resource URL path patterns
6353
*/
64-
public ResourceHandlerRegistration(ResourceLoader resourceLoader, String... pathPatterns) {
54+
public ResourceHandlerRegistration(String... pathPatterns) {
6555
Assert.notEmpty(pathPatterns, "At least one path pattern is required for resource handling.");
66-
this.resourceLoader = resourceLoader;
6756
this.pathPatterns = pathPatterns;
6857
}
6958

@@ -86,8 +75,7 @@ public ResourceHandlerRegistration(ResourceLoader resourceLoader, String... path
8675
* chained method invocation
8776
*/
8877
public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
89-
MvcNamespaceUtils.loadResourceLocations(
90-
resourceLocations, this.resourceLoader, this.locations, this.locationCharsets);
78+
this.locationValues.addAll(Arrays.asList(resourceLocations));
9179
return this;
9280
}
9381

@@ -177,8 +165,7 @@ protected ResourceHttpRequestHandler getRequestHandler() {
177165
handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers());
178166
handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers());
179167
}
180-
handler.setLocations(this.locations);
181-
handler.setLocationCharsets(this.locationCharsets);
168+
handler.setLocationValues(this.locationValues);
182169
if (this.cacheControl != null) {
183170
handler.setCacheControl(this.cacheControl);
184171
}

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,7 @@ public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletCon
114114
* registered resource handler
115115
*/
116116
public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) {
117-
ResourceHandlerRegistration registration =
118-
new ResourceHandlerRegistration(this.applicationContext, pathPatterns);
117+
ResourceHandlerRegistration registration = new ResourceHandlerRegistration(pathPatterns);
119118
this.registrations.add(registration);
120119
return registration;
121120
}

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.net.URLDecoder;
2121
import java.nio.charset.Charset;
2222
import java.util.ArrayList;
23-
import java.util.Collections;
2423
import java.util.HashMap;
2524
import java.util.List;
2625
import java.util.Map;
@@ -33,7 +32,10 @@
3332
import org.apache.commons.logging.LogFactory;
3433

3534
import org.springframework.beans.factory.InitializingBean;
35+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
36+
import org.springframework.context.ApplicationContext;
3637
import org.springframework.core.io.Resource;
38+
import org.springframework.core.io.UrlResource;
3739
import org.springframework.core.io.support.ResourceRegion;
3840
import org.springframework.http.HttpHeaders;
3941
import org.springframework.http.HttpMethod;
@@ -101,11 +103,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
101103

102104
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);
103105

106+
private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset=";
107+
104108

105109
private final List<Resource> locations = new ArrayList<Resource>(4);
106110

107111
private final Map<Resource, Charset> locationCharsets = new HashMap<Resource, Charset>(4);
108112

113+
private final List<String> locationValues = new ArrayList<String>(4);
114+
109115
private final List<ResourceResolver> resourceResolvers = new ArrayList<ResourceResolver>(4);
110116

111117
private final List<ResourceTransformer> resourceTransformers = new ArrayList<ResourceTransformer>(4);
@@ -129,8 +135,9 @@ public ResourceHttpRequestHandler() {
129135

130136

131137
/**
132-
* Set the {@code List} of {@code Resource} paths to use as sources
138+
* Set the {@code List} of {@code Resource} locations to use as sources
133139
* for serving static resources.
140+
* @see #setLocationValues(List)
134141
*/
135142
public void setLocations(List<Resource> locations) {
136143
Assert.notNull(locations, "Locations list must not be null");
@@ -139,35 +146,27 @@ public void setLocations(List<Resource> locations) {
139146
}
140147

141148
/**
142-
* Return the {@code List} of {@code Resource} paths to use as sources
143-
* for serving static resources.
149+
* Return the configured {@code List} of {@code Resource} locations.
150+
* Note that if {@link #setLocationValues(List) locationValues} are provided,
151+
* instead of loaded Resource-based locations, this method will return
152+
* empty until after initialization via {@link #afterPropertiesSet()}.
144153
*/
145154
public List<Resource> getLocations() {
146155
return this.locations;
147156
}
148157

149158
/**
150-
* Specify charsets associated with the configured {@link #setLocations(List)
151-
* locations}. This is supported for
152-
* {@link org.springframework.core.io.UrlResource URL resources} such as a
153-
* file or an HTTP URL location and is used in {@link PathResourceResolver}
154-
* to correctly encode paths relative to the location.
155-
* <p><strong>Note:</strong> The charset is used only if the
156-
* {@link #setUrlPathHelper urlPathHelper} property is also configured and
157-
* its {@code urlDecode} property is set to {@code true}.
158-
* @since 4.3.13
159-
*/
160-
public void setLocationCharsets(Map<Resource,Charset> locationCharsets) {
161-
this.locationCharsets.clear();
162-
this.locationCharsets.putAll(locationCharsets);
163-
}
164-
165-
/**
166-
* Return charsets associated with static resource locations.
159+
* An alternative to {@link #setLocations(List)} that accepts a list of
160+
* String-based location values, with support for {@link UrlResource}'s
161+
* (e.g. files or HTTP URLs) with a special prefix to indicate the charset
162+
* to use when appending relative paths. For example
163+
* {@code "[charset=Windows-31J]http://example.org/path"}.
167164
* @since 4.3.13
168165
*/
169-
public Map<Resource, Charset> getLocationCharsets() {
170-
return Collections.unmodifiableMap(this.locationCharsets);
166+
public void setLocationValues(List<String> locationValues) {
167+
Assert.notNull(locationValues, "Location values list must not be null");
168+
this.locationValues.clear();
169+
this.locationValues.addAll(locationValues);
171170
}
172171

173172
/**
@@ -297,6 +296,9 @@ public UrlPathHelper getUrlPathHelper() {
297296

298297
@Override
299298
public void afterPropertiesSet() throws Exception {
299+
300+
loadResourceLocations();
301+
300302
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
301303
logger.warn("Locations list is empty. No resources will be served unless a " +
302304
"custom ResourceResolver is configured as an alternative to PathResourceResolver.");
@@ -305,6 +307,7 @@ public void afterPropertiesSet() throws Exception {
305307
if (this.resourceResolvers.isEmpty()) {
306308
this.resourceResolvers.add(new PathResourceResolver());
307309
}
310+
308311
initAllowedLocations();
309312

310313
if (this.resourceHttpMessageConverter == null) {
@@ -317,6 +320,46 @@ public void afterPropertiesSet() throws Exception {
317320
this.contentNegotiationStrategy = initContentNegotiationStrategy();
318321
}
319322

323+
private void loadResourceLocations() {
324+
if (!CollectionUtils.isEmpty(this.locations) && !CollectionUtils.isEmpty(this.locationValues)) {
325+
throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " +
326+
"String-based \"locationValues\", but not both.");
327+
}
328+
if (CollectionUtils.isEmpty(this.locationValues)) {
329+
return;
330+
}
331+
ApplicationContext appContext = getApplicationContext();
332+
ConfigurableBeanFactory beanFactory = null;
333+
if (appContext.getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory) {
334+
beanFactory = ((ConfigurableBeanFactory) appContext.getAutowireCapableBeanFactory());
335+
}
336+
for (String location : this.locationValues) {
337+
if (beanFactory != null) {
338+
location = beanFactory.resolveEmbeddedValue(location);
339+
Assert.notNull(location, "Null location");
340+
}
341+
Charset charset = null;
342+
location = location.trim();
343+
if (location.startsWith(URL_RESOURCE_CHARSET_PREFIX)) {
344+
int endIndex = location.indexOf("]", URL_RESOURCE_CHARSET_PREFIX.length());
345+
if (endIndex == -1) {
346+
throw new IllegalArgumentException("Invalid charset syntax in location: " + location);
347+
}
348+
String value = location.substring(URL_RESOURCE_CHARSET_PREFIX.length(), endIndex);
349+
charset = Charset.forName(value);
350+
location = location.substring(endIndex + 1);
351+
}
352+
Resource resource = appContext.getResource(location);
353+
this.locations.add(resource);
354+
if (charset != null) {
355+
if (!(resource instanceof UrlResource)) {
356+
throw new IllegalArgumentException("Unexpected charset for non-UrlResource: " + resource);
357+
}
358+
this.locationCharsets.put(resource, charset);
359+
}
360+
}
361+
}
362+
320363
/**
321364
* Look for a {@code PathResourceResolver} among the configured resource
322365
* resolvers and set its {@code allowedLocations} property (if empty) to

0 commit comments

Comments
 (0)