1
1
/*
2
- * Copyright 2002-2020 the original author or authors.
2
+ * Copyright 2002-2021 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
21
21
import java .net .URLDecoder ;
22
22
import java .nio .charset .Charset ;
23
23
import java .util .ArrayList ;
24
- import java .util .Collections ;
25
24
import java .util .HashMap ;
26
25
import java .util .List ;
27
26
import java .util .Locale ;
28
27
import java .util .Map ;
29
- import java .util .stream .Collectors ;
30
28
31
29
import javax .servlet .ServletException ;
32
30
import javax .servlet .http .HttpServletRequest ;
69
67
* {@code HttpRequestHandler} that serves static resources in an optimized way
70
68
* according to the guidelines of Page Speed, YSlow, etc.
71
69
*
72
- * <p>The {@linkplain #setLocations "locations"} property takes a list of Spring
73
- * {@link Resource} locations from which static resources are allowed to be served
74
- * by this handler. Resources could be served from a classpath location, e.g.
70
+ * <p>The properties {@linkplain #setLocations "locations"} and
71
+ * {@linkplain #setLocationValues "locationValues"} accept locations from which
72
+ * static resources can be served by this handler. This can be relative to the
73
+ * root of the web application, or from the classpath, e.g.
75
74
* "classpath:/META-INF/public-web-resources/", allowing convenient packaging
76
75
* and serving of resources such as .js, .css, and others in jar files.
77
76
*
@@ -106,7 +105,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
106
105
107
106
private final List <String > locationValues = new ArrayList <>(4 );
108
107
109
- private final List <Resource > locations = new ArrayList <>(4 );
108
+ private final List <Resource > locationResources = new ArrayList <>(4 );
109
+
110
+ private final List <Resource > locationsToUse = new ArrayList <>(4 );
110
111
111
112
private final Map <Resource , Charset > locationCharsets = new HashMap <>(4 );
112
113
@@ -149,40 +150,52 @@ public ResourceHttpRequestHandler() {
149
150
150
151
151
152
/**
152
- * An alternative to {@link #setLocations(List)} that accepts a list of
153
- * String-based location values, with support for {@link UrlResource}'s
154
- * (e.g. files or HTTP URLs) with a special prefix to indicate the charset
155
- * to use when appending relative paths. For example
153
+ * Configure String-based locations to serve resources from.
154
+ * <p>For example, {{@code "/"}, {@code "classpath:/META-INF/public-web-resources/"}}
155
+ * allows resources to be served both from the web application root and
156
+ * from any JAR on the classpath that contains a
157
+ * {@code /META-INF/public-web-resources/} directory, with resources in the
158
+ * web application root taking precedence.
159
+ * <p>For {@link org.springframework.core.io.UrlResource URL-based resources}
160
+ * (e.g. files, HTTP URLs, etc) this method supports a special prefix to
161
+ * indicate the charset associated with the URL so that relative paths
162
+ * appended to it can be encoded correctly, for example
156
163
* {@code "[charset=Windows-31J]https://example.org/path"}.
157
164
* @since 4.3.13
165
+ * @see #setLocations(List)
158
166
*/
159
- public void setLocationValues (List <String > locationValues ) {
160
- Assert .notNull (locationValues , "Location values list must not be null" );
167
+ public void setLocationValues (List <String > locations ) {
168
+ Assert .notNull (locations , "Locations list must not be null" );
161
169
this .locationValues .clear ();
162
- this .locationValues .addAll (locationValues );
170
+ this .locationValues .addAll (locations );
163
171
}
164
172
165
173
/**
166
- * Set the {@code List} of {@code Resource} locations to use as sources
167
- * for serving static resources.
174
+ * Configure locations to serve resources from as pre-resourced Resource's.
168
175
* @see #setLocationValues(List)
169
176
*/
170
177
public void setLocations (List <Resource > locations ) {
171
178
Assert .notNull (locations , "Locations list must not be null" );
172
- this .locations .clear ();
173
- this .locations .addAll (locations );
179
+ this .locationResources .clear ();
180
+ this .locationResources .addAll (locations );
174
181
}
175
182
176
183
/**
177
- * Return the configured {@code List} of {@code Resource} locations.
178
- * <p>Note that if {@link #setLocationValues(List) locationValues} are provided,
179
- * instead of loaded Resource-based locations, this method will return
180
- * empty until after initialization via {@link #afterPropertiesSet()}.
184
+ * Return the configured {@code List} of {@code Resource} locations including
185
+ * both String-based locations provided via
186
+ * {@link #setLocationValues(List) setLocationValues} and pre-resolved {@code Resource}
187
+ * locations provided via {@link #setLocations(List) setLocations}.
188
+ * <p>Note that the returned list is fully initialized only after
189
+ * initialization via {@link #afterPropertiesSet()}.
181
190
* @see #setLocationValues
182
191
* @see #setLocations
183
192
*/
184
193
public List <Resource > getLocations () {
185
- return this .locations ;
194
+ if (this .locationsToUse .isEmpty ()) {
195
+ // Possibly not yet initialized, return only what we have so far
196
+ return this .locationResources ;
197
+ }
198
+ return this .locationsToUse ;
186
199
}
187
200
188
201
/**
@@ -374,7 +387,7 @@ public void setUseLastModified(boolean useLastModified) {
374
387
public void afterPropertiesSet () throws Exception {
375
388
resolveResourceLocations ();
376
389
377
- if (logger .isWarnEnabled () && CollectionUtils .isEmpty (this . locations )) {
390
+ if (logger .isWarnEnabled () && CollectionUtils .isEmpty (getLocations () )) {
378
391
logger .warn ("Locations list is empty. No resources will be served unless a " +
379
392
"custom ResourceResolver is configured as an alternative to PathResourceResolver." );
380
393
}
@@ -410,43 +423,38 @@ public void afterPropertiesSet() throws Exception {
410
423
}
411
424
412
425
private void resolveResourceLocations () {
413
- if (CollectionUtils .isEmpty (this .locationValues )) {
414
- return ;
415
- }
416
- else if (!CollectionUtils .isEmpty (this .locations )) {
417
- throw new IllegalArgumentException ("Please set either Resource-based \" locations\" or " +
418
- "String-based \" locationValues\" , but not both." );
419
- }
420
-
421
- ApplicationContext applicationContext = obtainApplicationContext ();
422
- for (String location : this .locationValues ) {
423
- if (this .embeddedValueResolver != null ) {
424
- String resolvedLocation = this .embeddedValueResolver .resolveStringValue (location );
425
- if (resolvedLocation == null ) {
426
- throw new IllegalArgumentException ("Location resolved to null: " + location );
426
+ if (!this .locationValues .isEmpty ()) {
427
+ ApplicationContext applicationContext = obtainApplicationContext ();
428
+ for (String location : this .locationValues ) {
429
+ if (this .embeddedValueResolver != null ) {
430
+ String resolvedLocation = this .embeddedValueResolver .resolveStringValue (location );
431
+ if (resolvedLocation == null ) {
432
+ throw new IllegalArgumentException ("Location resolved to null: " + location );
433
+ }
434
+ location = resolvedLocation ;
427
435
}
428
- location = resolvedLocation ;
429
- }
430
- Charset charset = null ;
431
- location = location .trim ();
432
- if (location .startsWith (URL_RESOURCE_CHARSET_PREFIX )) {
433
- int endIndex = location .indexOf (']' , URL_RESOURCE_CHARSET_PREFIX .length ());
434
- if (endIndex == -1 ) {
435
- throw new IllegalArgumentException ("Invalid charset syntax in location: " + location );
436
+ Charset charset = null ;
437
+ location = location .trim ();
438
+ if (location .startsWith (URL_RESOURCE_CHARSET_PREFIX )) {
439
+ int endIndex = location .indexOf (']' , URL_RESOURCE_CHARSET_PREFIX .length ());
440
+ if (endIndex == -1 ) {
441
+ throw new IllegalArgumentException ("Invalid charset syntax in location: " + location );
442
+ }
443
+ String value = location .substring (URL_RESOURCE_CHARSET_PREFIX .length (), endIndex );
444
+ charset = Charset .forName (value );
445
+ location = location .substring (endIndex + 1 );
436
446
}
437
- String value = location .substring (URL_RESOURCE_CHARSET_PREFIX .length (), endIndex );
438
- charset = Charset .forName (value );
439
- location = location .substring (endIndex + 1 );
440
- }
441
- Resource resource = applicationContext .getResource (location );
442
- this .locations .add (resource );
443
- if (charset != null ) {
444
- if (!(resource instanceof UrlResource )) {
445
- throw new IllegalArgumentException ("Unexpected charset for non-UrlResource: " + resource );
447
+ Resource resource = applicationContext .getResource (location );
448
+ this .locationsToUse .add (resource );
449
+ if (charset != null ) {
450
+ if (!(resource instanceof UrlResource )) {
451
+ throw new IllegalArgumentException ("Unexpected charset for non-UrlResource: " + resource );
452
+ }
453
+ this .locationCharsets .put (resource , charset );
446
454
}
447
- this .locationCharsets .put (resource , charset );
448
455
}
449
456
}
457
+ this .locationsToUse .addAll (this .locationResources );
450
458
}
451
459
452
460
/**
@@ -455,7 +463,7 @@ else if (!CollectionUtils.isEmpty(this.locations)) {
455
463
* match the {@link #setLocations locations} configured on this class.
456
464
*/
457
465
protected void initAllowedLocations () {
458
- if (CollectionUtils .isEmpty (this . locations )) {
466
+ if (CollectionUtils .isEmpty (getLocations () )) {
459
467
return ;
460
468
}
461
469
for (int i = getResourceResolvers ().size () - 1 ; i >= 0 ; i --) {
@@ -785,17 +793,6 @@ protected void setHeaders(HttpServletResponse response, Resource resource, @Null
785
793
786
794
@ Override
787
795
public String toString () {
788
- return "ResourceHttpRequestHandler " + formatLocations ();
796
+ return "ResourceHttpRequestHandler " + getLocations ();
789
797
}
790
-
791
- private Object formatLocations () {
792
- if (!this .locationValues .isEmpty ()) {
793
- return this .locationValues .stream ().collect (Collectors .joining ("\" , \" " , "[\" " , "\" ]" ));
794
- }
795
- else if (!this .locations .isEmpty ()) {
796
- return this .locations ;
797
- }
798
- return Collections .emptyList ();
799
- }
800
-
801
798
}
0 commit comments