Skip to content

Commit 6cb9a14

Browse files
committed
Expand static resource handling mechanism
An initial commit with expanded support for static resource handling: - Add ResourceResolver strategy for resolving a request to a Resource along with a few implementations. - Add PublicResourceUrlProvider to get URLs for client-side use. - Add ResourceUrlEncodingFilter and PublicResourceUrlProviderExposingInterceptor along with initial MVC Java config support. Issue: SPR-10933
1 parent f5cce14 commit 6cb9a14

34 files changed

+1898
-53
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ project("spring-orm-hibernate4") {
677677
optional("org.hibernate:hibernate-core:${hibernate4Version}")
678678
optional("org.hibernate:hibernate-entitymanager:${hibernate4Version}")
679679
optional("javax.servlet:javax.servlet-api:3.0.1")
680+
optional("aopalliance:aopalliance:1.0")
680681
}
681682
}
682683

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Wed Feb 12 23:28:21 CET 2014
1+
#Thu Mar 06 18:45:45 CET 2014
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip
6+
distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.messaging.converter;
18+
19+
import java.util.Collection;
20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
import org.junit.Before;
25+
import org.junit.Test;
26+
import org.springframework.messaging.Message;
27+
import org.springframework.messaging.MessageHeaders;
28+
import org.springframework.messaging.support.MessageBuilder;
29+
import org.springframework.util.MimeType;
30+
import org.springframework.util.MimeTypeUtils;
31+
32+
import static org.junit.Assert.*;
33+
34+
/**
35+
* Test fixture for {@link org.springframework.messaging.converter.AbstractMessageConverter}.
36+
*
37+
* @author Rossen Stoyanchev
38+
*/
39+
public class AbstractMessageConverterTests {
40+
41+
private TestMessageConverter converter;
42+
43+
44+
@Before
45+
public void setup() {
46+
this.converter = new TestMessageConverter();
47+
this.converter.setContentTypeResolver(new DefaultContentTypeResolver());
48+
}
49+
50+
@Test
51+
public void supportsTargetClass() {
52+
Message<String> message = MessageBuilder.withPayload("ABC").build();
53+
54+
assertEquals("success-from", this.converter.fromMessage(message, String.class));
55+
assertNull(this.converter.fromMessage(message, Integer.class));
56+
}
57+
58+
@Test
59+
public void supportsMimeType() {
60+
Message<String> message = MessageBuilder.withPayload(
61+
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build();
62+
63+
assertEquals("success-from", this.converter.fromMessage(message, String.class));
64+
}
65+
66+
@Test
67+
public void supportsMimeTypeNotSupported() {
68+
Message<String> message = MessageBuilder.withPayload(
69+
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build();
70+
71+
assertNull(this.converter.fromMessage(message, String.class));
72+
}
73+
74+
@Test
75+
public void supportsMimeTypeNotSpecified() {
76+
Message<String> message = MessageBuilder.withPayload("ABC").build();
77+
assertEquals("success-from", this.converter.fromMessage(message, String.class));
78+
}
79+
80+
@Test
81+
public void supportsMimeTypeNoneConfigured() {
82+
83+
Message<String> message = MessageBuilder.withPayload(
84+
"ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build();
85+
86+
this.converter = new TestMessageConverter(Collections.<MimeType>emptyList());
87+
this.converter.setContentTypeResolver(new DefaultContentTypeResolver());
88+
89+
assertEquals("success-from", this.converter.fromMessage(message, String.class));
90+
}
91+
92+
@Test
93+
public void toMessageHeadersCopied() {
94+
Map<String, Object> map = new HashMap<String, Object>();
95+
map.put("foo", "bar");
96+
MessageHeaders headers = new MessageHeaders(map );
97+
Message<?> message = this.converter.toMessage("ABC", headers);
98+
99+
assertEquals("bar", message.getHeaders().get("foo"));
100+
}
101+
102+
@Test
103+
public void toMessageContentTypeHeader() {
104+
Message<?> message = this.converter.toMessage("ABC", null);
105+
assertEquals(MimeTypeUtils.TEXT_PLAIN, message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
106+
}
107+
108+
109+
private static class TestMessageConverter extends AbstractMessageConverter {
110+
111+
public TestMessageConverter() {
112+
super(MimeTypeUtils.TEXT_PLAIN);
113+
}
114+
115+
public TestMessageConverter(Collection<MimeType> supportedMimeTypes) {
116+
super(supportedMimeTypes);
117+
}
118+
119+
@Override
120+
protected boolean supports(Class<?> clazz) {
121+
return String.class.equals(clazz);
122+
}
123+
124+
@Override
125+
public Object convertFromInternal(Message<?> message, Class<?> targetClass) {
126+
return "success-from";
127+
}
128+
129+
@Override
130+
public Object convertToInternal(Object payload, MessageHeaders headers) {
131+
return "success-to";
132+
}
133+
}
134+
135+
}

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

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,7 +23,9 @@
2323
import org.springframework.core.io.ResourceLoader;
2424
import org.springframework.util.Assert;
2525
import org.springframework.util.CollectionUtils;
26+
import org.springframework.web.servlet.resource.PathResourceResolver;
2627
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
28+
import org.springframework.web.servlet.resource.ResourceResolver;
2729

2830
/**
2931
* Encapsulates information required to create a resource handlers.
@@ -43,6 +45,9 @@ public class ResourceHandlerRegistration {
4345

4446
private Integer cachePeriod;
4547

48+
private List<ResourceResolver> resourceResolvers;
49+
50+
4651
/**
4752
* Create a {@link ResourceHandlerRegistration} instance.
4853
* @param resourceLoader a resource loader for turning a String location into a {@link Resource}
@@ -70,6 +75,17 @@ public ResourceHandlerRegistration addResourceLocations(String...resourceLocatio
7075
return this;
7176
}
7277

78+
/**
79+
* Configure the list of {@link ResourceResolver}s to use.
80+
* <p>
81+
* By default {@link PathResourceResolver} is configured. If using this property, it
82+
* is recommended to add {@link PathResourceResolver} as the last resolver.
83+
* @since 4.1
84+
*/
85+
public void setResourceResolvers(List<ResourceResolver> resourceResolvers) {
86+
this.resourceResolvers = resourceResolvers;
87+
}
88+
7389
/**
7490
* Specify the cache period for the resources served by the resource handler, in seconds. The default is to not
7591
* send any cache headers but to rely on last-modified timestamps only. Set to 0 in order to send cache headers
@@ -86,7 +102,11 @@ public ResourceHandlerRegistration setCachePeriod(Integer cachePeriod) {
86102
* Returns the URL path patterns for the resource handler.
87103
*/
88104
protected String[] getPathPatterns() {
89-
return pathPatterns;
105+
return this.pathPatterns;
106+
}
107+
108+
protected List<ResourceResolver> getResourceResolvers() {
109+
return this.resourceResolvers;
90110
}
91111

92112
/**
@@ -95,9 +115,12 @@ protected String[] getPathPatterns() {
95115
protected ResourceHttpRequestHandler getRequestHandler() {
96116
Assert.isTrue(!CollectionUtils.isEmpty(locations), "At least one location is required for resource handling.");
97117
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
98-
requestHandler.setLocations(locations);
99-
if (cachePeriod != null) {
100-
requestHandler.setCacheSeconds(cachePeriod);
118+
if (this.resourceResolvers != null) {
119+
requestHandler.setResourceResolvers(this.resourceResolvers);
120+
}
121+
requestHandler.setLocations(this.locations);
122+
if (this.cachePeriod != null) {
123+
requestHandler.setCacheSeconds(this.cachePeriod);
101124
}
102125
return requestHandler;
103126
}

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

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,13 +24,15 @@
2424

2525
import javax.servlet.ServletContext;
2626

27+
import org.springframework.beans.factory.BeanInitializationException;
2728
import org.springframework.context.ApplicationContext;
2829
import org.springframework.util.Assert;
2930
import org.springframework.web.HttpRequestHandler;
3031
import org.springframework.web.servlet.HandlerMapping;
3132
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
3233
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
3334
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
35+
import org.springframework.web.servlet.resource.ResourceResolver;
3436

3537
/**
3638
* Stores registrations of resource handlers for serving static resources such as images, css files and others
@@ -57,8 +59,11 @@ public class ResourceHandlerRegistry {
5759

5860
private final List<ResourceHandlerRegistration> registrations = new ArrayList<ResourceHandlerRegistration>();
5961

62+
private List<ResourceResolver> resourceResolvers;
63+
6064
private int order = Integer.MAX_VALUE -1;
6165

66+
6267
public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext) {
6368
Assert.notNull(applicationContext, "ApplicationContext is required");
6469
this.applicationContext = applicationContext;
@@ -98,6 +103,14 @@ public ResourceHandlerRegistry setOrder(int order) {
98103
return this;
99104
}
100105

106+
/**
107+
* Configure the {@link ResourceResolver}s to use by default in resource handlers that
108+
* don't have them set.
109+
*/
110+
public void setResourceResolvers(List<ResourceResolver> resourceResolvers) {
111+
this.resourceResolvers = resourceResolvers;
112+
}
113+
101114
/**
102115
* Return a handler mapping with the mapped resource handlers; or {@code null} in case of no registrations.
103116
*/
@@ -109,10 +122,19 @@ protected AbstractHandlerMapping getHandlerMapping() {
109122
Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<String, HttpRequestHandler>();
110123
for (ResourceHandlerRegistration registration : registrations) {
111124
for (String pathPattern : registration.getPathPatterns()) {
112-
ResourceHttpRequestHandler requestHandler = registration.getRequestHandler();
113-
requestHandler.setServletContext(servletContext);
114-
requestHandler.setApplicationContext(applicationContext);
115-
urlMap.put(pathPattern, requestHandler);
125+
ResourceHttpRequestHandler handler = registration.getRequestHandler();
126+
handler.setServletContext(servletContext);
127+
handler.setApplicationContext(applicationContext);
128+
if ((this.resourceResolvers != null) && (registration.getResourceResolvers() == null)) {
129+
handler.setResourceResolvers(this.resourceResolvers);
130+
}
131+
try {
132+
handler.afterPropertiesSet();
133+
}
134+
catch (Exception e) {
135+
throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", e);
136+
}
137+
urlMap.put(pathPattern, handler);
116138
}
117139
}
118140

0 commit comments

Comments
 (0)