Add a ResourceResolver implementation for WebJars
Prior to this commit, WebJars users needed to use versioned links within
templates for WebJars resources, such as `/jquery/1.2.0/jquery.js`.
This can be rather cumbersome when updating libraries - all references
in templates need to be updated.
One could use version-less links in templates, but needed to add a
specific MVC Handler that uses webjars.org's webjar-locator library.
While this approach makes maintaing templates easier, this makes HTTP
caching strategies less optimal.
This commit adds a new WebJarsResourceResolver that search for resources
located in WebJar locations. This ResourceResolver is automatically
registered if the "org.webjars:webjars-locator" dependency is present.
Registering WebJars resource handling can be done like this:
```java
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:META-INF/resources/webjars")
.resourceChain(true)
.addResolver(new WebJarsResourceResolver());
}
```
Issue: SPR-12323
polish
This commit is contained in:
@@ -119,6 +119,7 @@ import org.springframework.web.servlet.resource.ResourceTransformer;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlProvider;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor;
|
||||
import org.springframework.web.servlet.resource.VersionResourceResolver;
|
||||
import org.springframework.web.servlet.resource.WebJarsResourceResolver;
|
||||
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
|
||||
import org.springframework.web.servlet.view.BeanNameViewResolver;
|
||||
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
|
||||
@@ -409,10 +410,11 @@ public class MvcNamespaceTests {
|
||||
assertNotNull(handler);
|
||||
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers, Matchers.hasSize(3));
|
||||
assertThat(resolvers, Matchers.hasSize(4));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(CachingResourceResolver.class));
|
||||
assertThat(resolvers.get(1), Matchers.instanceOf(VersionResourceResolver.class));
|
||||
assertThat(resolvers.get(2), Matchers.instanceOf(PathResourceResolver.class));
|
||||
assertThat(resolvers.get(2), Matchers.instanceOf(WebJarsResourceResolver.class));
|
||||
assertThat(resolvers.get(3), Matchers.instanceOf(PathResourceResolver.class));
|
||||
|
||||
CachingResourceResolver cachingResolver = (CachingResourceResolver) resolvers.get(0);
|
||||
assertThat(cachingResolver.getCache(), Matchers.instanceOf(ConcurrentMapCache.class));
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
import org.springframework.web.servlet.resource.ResourceResolver;
|
||||
import org.springframework.web.servlet.resource.ResourceTransformer;
|
||||
import org.springframework.web.servlet.resource.VersionResourceResolver;
|
||||
import org.springframework.web.servlet.resource.WebJarsResourceResolver;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@@ -125,12 +126,13 @@ public class ResourceHandlerRegistryTests {
|
||||
|
||||
ResourceHttpRequestHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers.toString(), resolvers, Matchers.hasSize(3));
|
||||
assertThat(resolvers.toString(), resolvers, Matchers.hasSize(4));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(CachingResourceResolver.class));
|
||||
CachingResourceResolver cachingResolver = (CachingResourceResolver) resolvers.get(0);
|
||||
assertThat(cachingResolver.getCache(), Matchers.instanceOf(ConcurrentMapCache.class));
|
||||
assertThat(resolvers.get(1), Matchers.equalTo(mockResolver));
|
||||
assertThat(resolvers.get(2), Matchers.instanceOf(PathResourceResolver.class));
|
||||
assertThat(resolvers.get(2), Matchers.instanceOf(WebJarsResourceResolver.class));
|
||||
assertThat(resolvers.get(3), Matchers.instanceOf(PathResourceResolver.class));
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers, Matchers.hasSize(2));
|
||||
@@ -144,8 +146,9 @@ public class ResourceHandlerRegistryTests {
|
||||
|
||||
ResourceHttpRequestHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers, Matchers.hasSize(1));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(PathResourceResolver.class));
|
||||
assertThat(resolvers, Matchers.hasSize(2));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(WebJarsResourceResolver.class));
|
||||
assertThat(resolvers.get(1), Matchers.instanceOf(PathResourceResolver.class));
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers, Matchers.hasSize(0));
|
||||
@@ -162,10 +165,11 @@ public class ResourceHandlerRegistryTests {
|
||||
|
||||
ResourceHttpRequestHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
assertThat(resolvers.toString(), resolvers, Matchers.hasSize(3));
|
||||
assertThat(resolvers.toString(), resolvers, Matchers.hasSize(4));
|
||||
assertThat(resolvers.get(0), Matchers.instanceOf(CachingResourceResolver.class));
|
||||
assertThat(resolvers.get(1), Matchers.sameInstance(versionResolver));
|
||||
assertThat(resolvers.get(2), Matchers.instanceOf(PathResourceResolver.class));
|
||||
assertThat(resolvers.get(2), Matchers.instanceOf(WebJarsResourceResolver.class));
|
||||
assertThat(resolvers.get(3), Matchers.instanceOf(PathResourceResolver.class));
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers, Matchers.hasSize(3));
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Unit tests for
|
||||
* {@link org.springframework.web.servlet.resource.WebJarsResourceResolver}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class WebJarsResourceResolverTests {
|
||||
|
||||
private List<Resource> locations;
|
||||
|
||||
private WebJarsResourceResolver resolver;
|
||||
|
||||
private ResourceResolverChain chain;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// for this to work, an actual WebJar must be on the test classpath
|
||||
this.locations = Collections.singletonList(new ClassPathResource("/META-INF/resources/webjars/"));
|
||||
this.resolver = new WebJarsResourceResolver();
|
||||
this.chain = mock(ResourceResolverChain.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveUrlExisting() {
|
||||
this.locations = Collections.singletonList(new ClassPathResource("/META-INF/resources/webjars/", getClass()));
|
||||
String file = "/foo/2.3/foo.txt";
|
||||
given(this.chain.resolveUrlPath(file, this.locations)).willReturn(file);
|
||||
|
||||
String actual = this.resolver.resolveUrlPath(file, this.locations, this.chain);
|
||||
|
||||
assertEquals(file, actual);
|
||||
verify(this.chain, times(1)).resolveUrlPath(file, this.locations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveUrlExistingNotInJarFile() {
|
||||
this.locations = Collections.singletonList(new ClassPathResource("/META-INF/resources/webjars/", getClass()));
|
||||
String file = "/foo/foo.txt";
|
||||
given(this.chain.resolveUrlPath(file, this.locations)).willReturn(null);
|
||||
|
||||
String actual = this.resolver.resolveUrlPath(file, this.locations, this.chain);
|
||||
|
||||
assertNull(actual);
|
||||
verify(this.chain, times(1)).resolveUrlPath(file, this.locations);
|
||||
verify(this.chain, never()).resolveUrlPath("/foo/2.3/foo.txt", this.locations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveUrlWebJarResource() {
|
||||
String file = "/underscorejs/underscore.js";
|
||||
String expected = "/underscorejs/1.8.2/underscore.js";
|
||||
given(this.chain.resolveUrlPath(file, this.locations)).willReturn(null);
|
||||
given(this.chain.resolveUrlPath(expected, this.locations)).willReturn(expected);
|
||||
|
||||
String actual = this.resolver.resolveUrlPath(file, this.locations, this.chain);
|
||||
|
||||
assertEquals(expected, actual);
|
||||
verify(this.chain, times(1)).resolveUrlPath(file, this.locations);
|
||||
verify(this.chain, times(1)).resolveUrlPath(expected, this.locations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolverUrlWebJarResourceNotFound() {
|
||||
String file = "/something/something.js";
|
||||
given(this.chain.resolveUrlPath(file, this.locations)).willReturn(null);
|
||||
|
||||
String actual = this.resolver.resolveUrlPath(file, this.locations, this.chain);
|
||||
|
||||
assertNull(actual);
|
||||
verify(this.chain, times(1)).resolveUrlPath(file, this.locations);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user