20
20
import java .net .URLDecoder ;
21
21
import java .nio .charset .Charset ;
22
22
import java .util .ArrayList ;
23
- import java .util .Collections ;
24
23
import java .util .HashMap ;
25
24
import java .util .List ;
26
25
import java .util .Map ;
33
32
import org .apache .commons .logging .LogFactory ;
34
33
35
34
import org .springframework .beans .factory .InitializingBean ;
35
+ import org .springframework .beans .factory .config .ConfigurableBeanFactory ;
36
+ import org .springframework .context .ApplicationContext ;
36
37
import org .springframework .core .io .Resource ;
38
+ import org .springframework .core .io .UrlResource ;
37
39
import org .springframework .core .io .support .ResourceRegion ;
38
40
import org .springframework .http .HttpHeaders ;
39
41
import org .springframework .http .HttpMethod ;
@@ -101,11 +103,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
101
103
102
104
private static final Log logger = LogFactory .getLog (ResourceHttpRequestHandler .class );
103
105
106
+ private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset=" ;
107
+
104
108
105
109
private final List <Resource > locations = new ArrayList <Resource >(4 );
106
110
107
111
private final Map <Resource , Charset > locationCharsets = new HashMap <Resource , Charset >(4 );
108
112
113
+ private final List <String > locationValues = new ArrayList <String >(4 );
114
+
109
115
private final List <ResourceResolver > resourceResolvers = new ArrayList <ResourceResolver >(4 );
110
116
111
117
private final List <ResourceTransformer > resourceTransformers = new ArrayList <ResourceTransformer >(4 );
@@ -129,8 +135,9 @@ public ResourceHttpRequestHandler() {
129
135
130
136
131
137
/**
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
133
139
* for serving static resources.
140
+ * @see #setLocationValues(List)
134
141
*/
135
142
public void setLocations (List <Resource > locations ) {
136
143
Assert .notNull (locations , "Locations list must not be null" );
@@ -139,35 +146,27 @@ public void setLocations(List<Resource> locations) {
139
146
}
140
147
141
148
/**
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()}.
144
153
*/
145
154
public List <Resource > getLocations () {
146
155
return this .locations ;
147
156
}
148
157
149
158
/**
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"}.
167
164
* @since 4.3.13
168
165
*/
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 );
171
170
}
172
171
173
172
/**
@@ -297,6 +296,9 @@ public UrlPathHelper getUrlPathHelper() {
297
296
298
297
@ Override
299
298
public void afterPropertiesSet () throws Exception {
299
+
300
+ loadResourceLocations ();
301
+
300
302
if (logger .isWarnEnabled () && CollectionUtils .isEmpty (this .locations )) {
301
303
logger .warn ("Locations list is empty. No resources will be served unless a " +
302
304
"custom ResourceResolver is configured as an alternative to PathResourceResolver." );
@@ -305,6 +307,7 @@ public void afterPropertiesSet() throws Exception {
305
307
if (this .resourceResolvers .isEmpty ()) {
306
308
this .resourceResolvers .add (new PathResourceResolver ());
307
309
}
310
+
308
311
initAllowedLocations ();
309
312
310
313
if (this .resourceHttpMessageConverter == null ) {
@@ -317,6 +320,46 @@ public void afterPropertiesSet() throws Exception {
317
320
this .contentNegotiationStrategy = initContentNegotiationStrategy ();
318
321
}
319
322
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
+
320
363
/**
321
364
* Look for a {@code PathResourceResolver} among the configured resource
322
365
* resolvers and set its {@code allowedLocations} property (if empty) to
0 commit comments