|
|
|
@ -20,7 +20,6 @@ import java.io.IOException; |
|
|
|
import java.net.URLDecoder; |
|
|
|
import java.net.URLDecoder; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Collections; |
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
@ -33,7 +32,10 @@ import org.apache.commons.logging.Log; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.InitializingBean; |
|
|
|
import org.springframework.beans.factory.InitializingBean; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory; |
|
|
|
|
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
import org.springframework.core.io.Resource; |
|
|
|
import org.springframework.core.io.Resource; |
|
|
|
|
|
|
|
import org.springframework.core.io.UrlResource; |
|
|
|
import org.springframework.core.io.support.ResourceRegion; |
|
|
|
import org.springframework.core.io.support.ResourceRegion; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
|
import org.springframework.http.HttpMethod; |
|
|
|
import org.springframework.http.HttpMethod; |
|
|
|
@ -101,11 +103,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
|
|
|
|
|
|
|
|
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class); |
|
|
|
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset="; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<Resource> locations = new ArrayList<Resource>(4); |
|
|
|
private final List<Resource> locations = new ArrayList<Resource>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final Map<Resource, Charset> locationCharsets = new HashMap<Resource, Charset>(4); |
|
|
|
private final Map<Resource, Charset> locationCharsets = new HashMap<Resource, Charset>(4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final List<String> locationValues = new ArrayList<String>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final List<ResourceResolver> resourceResolvers = new ArrayList<ResourceResolver>(4); |
|
|
|
private final List<ResourceResolver> resourceResolvers = new ArrayList<ResourceResolver>(4); |
|
|
|
|
|
|
|
|
|
|
|
private final List<ResourceTransformer> resourceTransformers = new ArrayList<ResourceTransformer>(4); |
|
|
|
private final List<ResourceTransformer> resourceTransformers = new ArrayList<ResourceTransformer>(4); |
|
|
|
@ -129,8 +135,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Set the {@code List} of {@code Resource} paths to use as sources |
|
|
|
* Set the {@code List} of {@code Resource} locations to use as sources |
|
|
|
* for serving static resources. |
|
|
|
* for serving static resources. |
|
|
|
|
|
|
|
* @see #setLocationValues(List) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setLocations(List<Resource> locations) { |
|
|
|
public void setLocations(List<Resource> locations) { |
|
|
|
Assert.notNull(locations, "Locations list must not be null"); |
|
|
|
Assert.notNull(locations, "Locations list must not be null"); |
|
|
|
@ -139,35 +146,27 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Return the {@code List} of {@code Resource} paths to use as sources |
|
|
|
* Return the configured {@code List} of {@code Resource} locations. |
|
|
|
* for serving static resources. |
|
|
|
* Note that if {@link #setLocationValues(List) locationValues} are provided, |
|
|
|
|
|
|
|
* instead of loaded Resource-based locations, this method will return |
|
|
|
|
|
|
|
* empty until after initialization via {@link #afterPropertiesSet()}. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public List<Resource> getLocations() { |
|
|
|
public List<Resource> getLocations() { |
|
|
|
return this.locations; |
|
|
|
return this.locations; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Specify charsets associated with the configured {@link #setLocations(List) |
|
|
|
* An alternative to {@link #setLocations(List)} that accepts a list of |
|
|
|
* locations}. This is supported for |
|
|
|
* String-based location values, with support for {@link UrlResource}'s |
|
|
|
* {@link org.springframework.core.io.UrlResource URL resources} such as a |
|
|
|
* (e.g. files or HTTP URLs) with a special prefix to indicate the charset |
|
|
|
* file or an HTTP URL location and is used in {@link PathResourceResolver} |
|
|
|
* to use when appending relative paths. For example |
|
|
|
* to correctly encode paths relative to the location. |
|
|
|
* {@code "[charset=Windows-31J]http://example.org/path"}. |
|
|
|
* <p><strong>Note:</strong> The charset is used only if the |
|
|
|
|
|
|
|
* {@link #setUrlPathHelper urlPathHelper} property is also configured and |
|
|
|
|
|
|
|
* its {@code urlDecode} property is set to {@code true}. |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setLocationCharsets(Map<Resource,Charset> locationCharsets) { |
|
|
|
|
|
|
|
this.locationCharsets.clear(); |
|
|
|
|
|
|
|
this.locationCharsets.putAll(locationCharsets); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Return charsets associated with static resource locations. |
|
|
|
|
|
|
|
* @since 4.3.13 |
|
|
|
* @since 4.3.13 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public Map<Resource, Charset> getLocationCharsets() { |
|
|
|
public void setLocationValues(List<String> locationValues) { |
|
|
|
return Collections.unmodifiableMap(this.locationCharsets); |
|
|
|
Assert.notNull(locationValues, "Location values list must not be null"); |
|
|
|
|
|
|
|
this.locationValues.clear(); |
|
|
|
|
|
|
|
this.locationValues.addAll(locationValues); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -297,6 +296,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void afterPropertiesSet() throws Exception { |
|
|
|
public void afterPropertiesSet() throws Exception { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loadResourceLocations(); |
|
|
|
|
|
|
|
|
|
|
|
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) { |
|
|
|
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) { |
|
|
|
logger.warn("Locations list is empty. No resources will be served unless a " + |
|
|
|
logger.warn("Locations list is empty. No resources will be served unless a " + |
|
|
|
"custom ResourceResolver is configured as an alternative to PathResourceResolver."); |
|
|
|
"custom ResourceResolver is configured as an alternative to PathResourceResolver."); |
|
|
|
@ -305,6 +307,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
if (this.resourceResolvers.isEmpty()) { |
|
|
|
if (this.resourceResolvers.isEmpty()) { |
|
|
|
this.resourceResolvers.add(new PathResourceResolver()); |
|
|
|
this.resourceResolvers.add(new PathResourceResolver()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
initAllowedLocations(); |
|
|
|
initAllowedLocations(); |
|
|
|
|
|
|
|
|
|
|
|
if (this.resourceHttpMessageConverter == null) { |
|
|
|
if (this.resourceHttpMessageConverter == null) { |
|
|
|
@ -317,6 +320,46 @@ public class ResourceHttpRequestHandler extends WebContentGenerator |
|
|
|
this.contentNegotiationStrategy = initContentNegotiationStrategy(); |
|
|
|
this.contentNegotiationStrategy = initContentNegotiationStrategy(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void loadResourceLocations() { |
|
|
|
|
|
|
|
if (!CollectionUtils.isEmpty(this.locations) && !CollectionUtils.isEmpty(this.locationValues)) { |
|
|
|
|
|
|
|
throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " + |
|
|
|
|
|
|
|
"String-based \"locationValues\", but not both."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(this.locationValues)) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ApplicationContext appContext = getApplicationContext(); |
|
|
|
|
|
|
|
ConfigurableBeanFactory beanFactory = null; |
|
|
|
|
|
|
|
if (appContext.getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory) { |
|
|
|
|
|
|
|
beanFactory = ((ConfigurableBeanFactory) appContext.getAutowireCapableBeanFactory()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (String location : this.locationValues) { |
|
|
|
|
|
|
|
if (beanFactory != null) { |
|
|
|
|
|
|
|
location = beanFactory.resolveEmbeddedValue(location); |
|
|
|
|
|
|
|
Assert.notNull(location, "Null location"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Charset charset = null; |
|
|
|
|
|
|
|
location = location.trim(); |
|
|
|
|
|
|
|
if (location.startsWith(URL_RESOURCE_CHARSET_PREFIX)) { |
|
|
|
|
|
|
|
int endIndex = location.indexOf("]", URL_RESOURCE_CHARSET_PREFIX.length()); |
|
|
|
|
|
|
|
if (endIndex == -1) { |
|
|
|
|
|
|
|
throw new IllegalArgumentException("Invalid charset syntax in location: " + location); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
String value = location.substring(URL_RESOURCE_CHARSET_PREFIX.length(), endIndex); |
|
|
|
|
|
|
|
charset = Charset.forName(value); |
|
|
|
|
|
|
|
location = location.substring(endIndex + 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Resource resource = appContext.getResource(location); |
|
|
|
|
|
|
|
this.locations.add(resource); |
|
|
|
|
|
|
|
if (charset != null) { |
|
|
|
|
|
|
|
if (!(resource instanceof UrlResource)) { |
|
|
|
|
|
|
|
throw new IllegalArgumentException("Unexpected charset for non-UrlResource: " + resource); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.locationCharsets.put(resource, charset); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Look for a {@code PathResourceResolver} among the configured resource |
|
|
|
* Look for a {@code PathResourceResolver} among the configured resource |
|
|
|
* resolvers and set its {@code allowedLocations} property (if empty) to |
|
|
|
* resolvers and set its {@code allowedLocations} property (if empty) to |
|
|
|
|