Extract base interface for VersionStrategy
This change splits out a base VersionPathStrategy interface that focuses on where in the URL path the version is embedded.
This commit is contained in:
committed by
Brian Clozel
parent
9f3c1cf762
commit
d917b8a923
@@ -6,6 +6,7 @@ import java.util.regex.Pattern;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -20,73 +21,106 @@ import org.springframework.util.StringUtils;
|
||||
* version string.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public abstract class AbstractVersionStrategy implements VersionStrategy {
|
||||
|
||||
private static final Pattern pattern = Pattern.compile("-(\\S*)\\.");
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final VersionPathStrategy pathStrategy;
|
||||
|
||||
|
||||
protected AbstractVersionStrategy(VersionPathStrategy pathStrategy) {
|
||||
Assert.notNull(pathStrategy, "'pathStrategy' is required");
|
||||
this.pathStrategy = pathStrategy;
|
||||
}
|
||||
|
||||
|
||||
public VersionPathStrategy getVersionPathStrategy() {
|
||||
return this.pathStrategy;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String extractVersion(String requestPath) {
|
||||
return this.pathStrategy.extractVersion(requestPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String removeVersion(String requestPath, String version) {
|
||||
return this.pathStrategy.removeVersion(requestPath, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String addVersion(String requestPath, String version) {
|
||||
return this.pathStrategy.addVersion(requestPath, version);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts a version string from the request path, as a suffix in the resource
|
||||
* file name.
|
||||
* @param requestPath the request path to extract the version string from
|
||||
* @return a version string or an empty string if none was found
|
||||
* A prefix-based {@code VersionPathStrategy},
|
||||
* e.g. {@code "{version}/path/foo.js"}.
|
||||
*/
|
||||
protected String extractVersionFromFilename(String requestPath) {
|
||||
Matcher matcher = pattern.matcher(requestPath);
|
||||
if (matcher.find()) {
|
||||
String match = matcher.group(1);
|
||||
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match;
|
||||
protected static class PrefixVersionPathStrategy implements VersionPathStrategy {
|
||||
|
||||
private final String prefix;
|
||||
|
||||
|
||||
public PrefixVersionPathStrategy(String version) {
|
||||
Assert.hasText(version, "'version' is required and must not be empty");
|
||||
this.prefix = version;
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
|
||||
@Override
|
||||
public String extractVersion(String requestPath) {
|
||||
return (requestPath.startsWith(this.prefix) ? this.prefix : null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given candidate version string from the request path.
|
||||
* The version string should be a suffix in the resource file name.
|
||||
*/
|
||||
protected String deleteVersionFromFilename(String requestPath, String candidateVersion) {
|
||||
return StringUtils.delete(requestPath, "-" + candidateVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given version string to the baseUrl, as a file name suffix.
|
||||
*/
|
||||
protected String addVersionToFilename(String baseUrl, String version) {
|
||||
String baseFilename = StringUtils.stripFilenameExtension(baseUrl);
|
||||
String extension = StringUtils.getFilenameExtension(baseUrl);
|
||||
return baseFilename + "-" + version + "." + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a version string from the request path, as a prefix in the request path.
|
||||
* @param requestPath the request path to extract the version string from
|
||||
* @return a version string or an empty string if none was found
|
||||
*/
|
||||
protected String extractVersionAsPrefix(String requestPath, String prefix) {
|
||||
if (requestPath.startsWith(prefix)) {
|
||||
return prefix;
|
||||
@Override
|
||||
public String removeVersion(String requestPath, String version) {
|
||||
return requestPath.substring(this.prefix.length());
|
||||
}
|
||||
return "";
|
||||
|
||||
@Override
|
||||
public String addVersion(String path, String version) {
|
||||
return (this.prefix.endsWith("/") || path.startsWith("/") ? this.prefix + path : this.prefix + "/" + path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given candidate version string from the request path.
|
||||
* The version string should be a prefix in the request path.
|
||||
* File name-based {@code VersionPathStrategy},
|
||||
* e.g. {@code "path/foo-{version}.css"}.
|
||||
*/
|
||||
protected String deleteVersionAsPrefix(String requestPath, String version) {
|
||||
return requestPath.substring(version.length());
|
||||
}
|
||||
protected static class FileNameVersionPathStrategy implements VersionPathStrategy {
|
||||
|
||||
/**
|
||||
* Adds the given version string to the baseUrl, as a prefix in the request path.
|
||||
*/
|
||||
protected String addVersionAsPrefix(String baseUrl, String version) {
|
||||
return version + baseUrl;
|
||||
private static final Pattern pattern = Pattern.compile("-(\\S*)\\.");
|
||||
|
||||
|
||||
@Override
|
||||
public String extractVersion(String requestPath) {
|
||||
Matcher matcher = pattern.matcher(requestPath);
|
||||
if (matcher.find()) {
|
||||
String match = matcher.group(1);
|
||||
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String removeVersion(String requestPath, String version) {
|
||||
return StringUtils.delete(requestPath, "-" + version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String addVersion(String requestPath, String version) {
|
||||
String baseFilename = StringUtils.stripFilenameExtension(requestPath);
|
||||
String extension = StringUtils.getFilenameExtension(requestPath);
|
||||
return baseFilename + "-" + version + "." + extension;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2014 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 java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* A {@code VersionStrategy} that handles version strings as a Hex MD5 hash in resource file names.
|
||||
*
|
||||
* <p>For example the path "styles/foo-e36d2e05253c6c7085a91522ce43a0b4.css" will
|
||||
* match to "styles/foo.css" assuming the hash computed from the content of
|
||||
* "foo.css" matches the hash in the path.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @author Brian Clozel
|
||||
* @since 4.1
|
||||
* @see VersionResourceResolver
|
||||
*/
|
||||
public class ContentBasedVersionStrategy extends AbstractVersionStrategy {
|
||||
|
||||
@Override
|
||||
public String extractVersionFromPath(String requestPath) {
|
||||
return extractVersionFromFilename(requestPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteVersionFromPath(String requestPath, String candidateVersion) {
|
||||
return deleteVersionFromFilename(requestPath, candidateVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resourceVersionMatches(Resource baseResource, String candidateVersion) {
|
||||
String resourceHash = calculateHash(baseResource);
|
||||
return candidateVersion.equals(resourceHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String addVersionToUrl(String baseUrl, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Getting the original resource to calculate hash");
|
||||
}
|
||||
Resource original = chain.resolveResource(null, baseUrl, locations);
|
||||
String hash = calculateHash(original);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Calculated hash=" + hash);
|
||||
}
|
||||
return addVersionToFilename(baseUrl, hash);
|
||||
}
|
||||
|
||||
private String calculateHash(Resource resource) {
|
||||
try {
|
||||
byte[] content = FileCopyUtils.copyToByteArray(resource.getInputStream());
|
||||
return DigestUtils.md5DigestAsHex(content);
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Failed to calculate hash for resource [" + resource + "]");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2014 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 java.io.IOException;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* A {@code VersionStrategy} that calculates an Hex MD5 hashes from the content
|
||||
* of the resource and appends it to the file name, e.g.
|
||||
* {@code "styles/main-e36d2e05253c6c7085a91522ce43a0b4.css"}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
* @see VersionResourceResolver
|
||||
*/
|
||||
public class ContentVersionStrategy extends AbstractVersionStrategy {
|
||||
|
||||
|
||||
public ContentVersionStrategy() {
|
||||
super(new FileNameVersionPathStrategy());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getResourceVersion(Resource resource) {
|
||||
try {
|
||||
byte[] content = FileCopyUtils.copyToByteArray(resource.getInputStream());
|
||||
return DigestUtils.md5DigestAsHex(content);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to calculate hash for resource [" + resource + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,28 +15,18 @@
|
||||
*/
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@code VersionStrategy} that handles unique, static, application-wide version strings
|
||||
* as prefixes in the request path.
|
||||
* A {@code VersionStrategy} that relies on a fixed version applied as a request
|
||||
* path prefix, e.g. reduced SHA, version name, release date, etc.
|
||||
*
|
||||
* <p>Enables inserting a unique and static version String (e.g. reduced SHA, version name,
|
||||
* release date) at the beginning of resource paths so that when a new version of the application
|
||||
* is released, clients are forced to reload application resources.
|
||||
*
|
||||
* <p>This is useful when changing resource names is not an option (e.g. when
|
||||
* using JavaScript module loaders). If that's not the case, the use of
|
||||
* {@link ContentBasedVersionStrategy} provides more optimal performance since
|
||||
* version is generated on a per-resource basis: only actually modified resources are reloaded
|
||||
* by the client.
|
||||
* <p>This is useful for example when {@link ContentVersionStrategy} cannot be
|
||||
* used such as when using JavaScript module loaders which are in charge of
|
||||
* loading the JavaScript resources and need to know their relative paths.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sam Brannen
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
* @see VersionResourceResolver
|
||||
*/
|
||||
@@ -46,44 +36,17 @@ public class FixedVersionStrategy extends AbstractVersionStrategy {
|
||||
|
||||
/**
|
||||
* Create a new FixedVersionStrategy with the given version string.
|
||||
* @param fixedVersion the fixed version string to use
|
||||
* @param version the fixed version string to use
|
||||
*/
|
||||
public FixedVersionStrategy(String fixedVersion) {
|
||||
Assert.hasText(fixedVersion, "version must not be null or empty");
|
||||
this.version = fixedVersion.endsWith("/") ? fixedVersion : fixedVersion + "/";
|
||||
public FixedVersionStrategy(String version) {
|
||||
super(new PrefixVersionPathStrategy(version));
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FixedVersionStrategy and get the version string to use by
|
||||
* calling the given {@code Callable} instance.
|
||||
*/
|
||||
public FixedVersionStrategy(Callable<String> versionInitializer) throws Exception {
|
||||
String fixedVersion = versionInitializer.call();
|
||||
Assert.hasText(fixedVersion, "version must not be null or empty");
|
||||
this.version = fixedVersion.endsWith("/") ? fixedVersion : fixedVersion + "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractVersionFromPath(String requestPath) {
|
||||
|
||||
return extractVersionAsPrefix(requestPath, this.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteVersionFromPath(String requestPath, String candidateVersion) {
|
||||
return deleteVersionAsPrefix(requestPath, candidateVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resourceVersionMatches(Resource baseResource, String candidateVersion) {
|
||||
return this.version.equals(candidateVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String addVersionToUrl(String baseUrl, List<? extends Resource> locations,
|
||||
ResourceResolverChain chain) {
|
||||
|
||||
return addVersionAsPrefix(baseUrl, this.version);
|
||||
public String getResourceVersion(Resource resource) {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2014 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;
|
||||
|
||||
/**
|
||||
* A strategy for extracting and embedding a resource version in its URL path.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface VersionPathStrategy {
|
||||
|
||||
/**
|
||||
* Extract the resource version from the request path.
|
||||
* @param requestPath the request path to check
|
||||
* @return the version string or {@code null} if none was found
|
||||
*/
|
||||
String extractVersion(String requestPath);
|
||||
|
||||
/**
|
||||
* Remove the version from the request path. It is assumed that the given
|
||||
* version was extracted via {@link #extractVersion(String)}.
|
||||
* @param requestPath the request path of the resource being resolved
|
||||
* @param version the version obtained from {@link #extractVersion(String)}
|
||||
* @return the request path with the version removed
|
||||
*/
|
||||
String removeVersion(String requestPath, String version);
|
||||
|
||||
/**
|
||||
* Add a version to the given request path.
|
||||
* @param requestPath the requestPath
|
||||
* @param version the version
|
||||
* @return the requestPath updated with a version string
|
||||
*/
|
||||
String addVersion(String requestPath, String version);
|
||||
|
||||
}
|
||||
@@ -50,6 +50,7 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
|
||||
private Map<String, VersionStrategy> versionStrategyMap = Collections.emptyMap();
|
||||
|
||||
|
||||
/**
|
||||
* Set a Map with URL paths as keys and {@code VersionStrategy}
|
||||
* as values.
|
||||
@@ -59,10 +60,11 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
*
|
||||
* @param versionStrategyMap map with URLs as keys and version strategies as values
|
||||
*/
|
||||
public void setVersionStrategyMap(Map<String, VersionStrategy> versionStrategyMap) {
|
||||
public void setStrategyMap(Map<String, VersionStrategy> versionStrategyMap) {
|
||||
this.versionStrategyMap = versionStrategyMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
@@ -72,12 +74,12 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
VersionStrategy versionStrategy = findStrategy(requestPath);
|
||||
VersionStrategy versionStrategy = getStrategyForPath(requestPath);
|
||||
if (versionStrategy == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String candidateVersion = versionStrategy.extractVersionFromPath(requestPath);
|
||||
String candidateVersion = versionStrategy.extractVersion(requestPath);
|
||||
if (StringUtils.isEmpty(candidateVersion)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No version found in path=\"" + requestPath + "\"");
|
||||
@@ -85,7 +87,7 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
String simplePath = versionStrategy.deleteVersionFromPath(requestPath, candidateVersion);
|
||||
String simplePath = versionStrategy.removeVersion(requestPath, candidateVersion);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Extracted version from path, re-resolving without version, path=\"" + simplePath + "\"");
|
||||
@@ -96,7 +98,8 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (versionStrategy.resourceVersionMatches(baseResource, candidateVersion)) {
|
||||
String actualVersion = versionStrategy.getResourceVersion(baseResource);
|
||||
if (candidateVersion.equals(actualVersion)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("resource matches extracted version");
|
||||
}
|
||||
@@ -113,11 +116,19 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
protected String resolveUrlPathInternal(String resourceUrlPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
String baseUrl = chain.resolveUrlPath(resourceUrlPath, locations);
|
||||
if (StringUtils.hasText(baseUrl)) {
|
||||
VersionStrategy versionStrategy = findStrategy(resourceUrlPath);
|
||||
VersionStrategy versionStrategy = getStrategyForPath(resourceUrlPath);
|
||||
if (versionStrategy == null) {
|
||||
return null;
|
||||
}
|
||||
return versionStrategy.addVersionToUrl(baseUrl, locations, chain);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Getting the original resource to determine version");
|
||||
}
|
||||
Resource resource = chain.resolveResource(null, baseUrl, locations);
|
||||
String version = versionStrategy.getResourceVersion(resource);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Version=" + version);
|
||||
}
|
||||
return versionStrategy.addVersion(baseUrl, version);
|
||||
}
|
||||
return baseUrl;
|
||||
}
|
||||
@@ -126,7 +137,7 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
* Finds a {@code VersionStrategy} for the request path of the requested resource.
|
||||
* @return an instance of a {@code VersionStrategy} or null if none matches that request path
|
||||
*/
|
||||
protected VersionStrategy findStrategy(String requestPath) {
|
||||
protected VersionStrategy getStrategyForPath(String requestPath) {
|
||||
String path = "/".concat(requestPath);
|
||||
List<String> matchingPatterns = new ArrayList<String>();
|
||||
for (String pattern : this.versionStrategyMap.keySet()) {
|
||||
|
||||
@@ -15,50 +15,24 @@
|
||||
*/
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* A strategy for handling version strings in request paths when resolving
|
||||
* static resources with a {@link VersionResourceResolver}.
|
||||
* An extension of {@link VersionPathStrategy} that adds a method to determine the
|
||||
* actual version of a {@link org.springframework.core.io.Resource Resource}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1
|
||||
* @see VersionResourceResolver
|
||||
*/
|
||||
public interface VersionStrategy {
|
||||
public interface VersionStrategy extends VersionPathStrategy {
|
||||
|
||||
/**
|
||||
* Extracts a version string from the request path.
|
||||
* @param requestPath the request path of the resource being resolved
|
||||
* @return the version string or an empty string if none was found
|
||||
* Get the version for the given resource.
|
||||
* @param resource the resource to check
|
||||
* @return the version, never {@code null}
|
||||
*/
|
||||
String extractVersionFromPath(String requestPath);
|
||||
String getResourceVersion(Resource resource);
|
||||
|
||||
/**
|
||||
* Deletes the given candidate version string from the given request path.
|
||||
* @param requestPath the request path of the resource being resolved
|
||||
* @param candidateVersion the candidate version string
|
||||
* @return the modified request path, without the version string
|
||||
*/
|
||||
String deleteVersionFromPath(String requestPath, String candidateVersion);
|
||||
|
||||
/**
|
||||
* Checks whether the given {@code Resource} matches the candidate version string.
|
||||
* Useful when the version string is managed on a per-resource basis.
|
||||
* @param baseResource the resource to check against the given version
|
||||
* @param candidateVersion the candidate version for the given resource
|
||||
* @return true if the resource matches the version string, false otherwise
|
||||
*/
|
||||
boolean resourceVersionMatches(Resource baseResource, String candidateVersion);
|
||||
|
||||
/**
|
||||
* Adds a version string to the given baseUrl.
|
||||
* @param baseUrl the baseUrl of the requested resource
|
||||
* @param locations the resource locations to resolve resources from
|
||||
* @param chain the chain of resource resolvers
|
||||
* @return the baseUrl updated with a version string
|
||||
*/
|
||||
String addVersionToUrl(String baseUrl, List<? extends Resource> locations, ResourceResolverChain chain);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class AppCacheManifestTransformerTests {
|
||||
|
||||
VersionResourceResolver versionResourceResolver = new VersionResourceResolver();
|
||||
versionResourceResolver
|
||||
.setVersionStrategyMap(Collections.singletonMap("/**", new ContentBasedVersionStrategy()));
|
||||
.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));
|
||||
|
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||
resolvers.add(versionResourceResolver);
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
*/
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -31,65 +28,50 @@ import org.springframework.util.FileCopyUtils;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.web.servlet.resource.ContentBasedVersionStrategy}
|
||||
* Unit tests for {@link ContentVersionStrategy}
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ContentBasedVersionStrategyTests {
|
||||
|
||||
private List<Resource> locations;
|
||||
private ContentVersionStrategy versionStrategy = new ContentVersionStrategy();
|
||||
|
||||
private ContentBasedVersionStrategy versionStrategy = new ContentBasedVersionStrategy();
|
||||
|
||||
private ResourceResolverChain chain;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.locations = new ArrayList<Resource>();
|
||||
this.locations.add(new ClassPathResource("test/", getClass()));
|
||||
this.locations.add(new ClassPathResource("testalternatepath/", getClass()));
|
||||
|
||||
VersionResourceResolver versionResourceResolver = new VersionResourceResolver();
|
||||
versionResourceResolver.setVersionStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
this.chain = new DefaultResourceResolverChain(Arrays.asList(versionResourceResolver, new PathResourceResolver()));
|
||||
versionResourceResolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractVersionFromPath() throws Exception {
|
||||
public void extractVersion() throws Exception {
|
||||
String hash = "7fbe76cdac6093784895bb4989203e5a";
|
||||
String path = "font-awesome/css/font-awesome.min-" + hash + ".css";
|
||||
|
||||
assertEquals(hash, this.versionStrategy.extractVersionFromPath(path));
|
||||
assertEquals("", this.versionStrategy.extractVersionFromPath("foo/bar.css"));
|
||||
assertEquals(hash, this.versionStrategy.extractVersion(path));
|
||||
assertNull(this.versionStrategy.extractVersion("foo/bar.css"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteVersionFromPath() throws Exception {
|
||||
public void removeVersion() throws Exception {
|
||||
String file = "font-awesome/css/font-awesome.min%s%s.css";
|
||||
String hash = "7fbe76cdac6093784895bb4989203e5a";
|
||||
|
||||
assertEquals(String.format(file, "", ""), this.versionStrategy.deleteVersionFromPath(String.format(file, "-", hash), hash));
|
||||
assertEquals("", this.versionStrategy.extractVersionFromPath("foo/bar.css"));
|
||||
assertEquals(String.format(file, "", ""), this.versionStrategy.removeVersion(String.format(file, "-", hash), hash));
|
||||
assertNull(this.versionStrategy.extractVersion("foo/bar.css"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceVersionMatches() throws Exception {
|
||||
public void getResourceVersion() throws Exception {
|
||||
Resource expected = new ClassPathResource("test/bar.css", getClass());
|
||||
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream()));
|
||||
String wrongHash = "wronghash";
|
||||
|
||||
assertTrue(this.versionStrategy.resourceVersionMatches(expected, hash));
|
||||
assertFalse(this.versionStrategy.resourceVersionMatches(expected, wrongHash));
|
||||
assertEquals(hash, this.versionStrategy.getResourceVersion(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addVersionToUrl() throws Exception {
|
||||
String file = "bar.css";
|
||||
Resource expected = new ClassPathResource("test/" + file, getClass());
|
||||
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream()));
|
||||
String path = "bar-" + hash + ".css";
|
||||
String resultUrl = this.versionStrategy.addVersionToUrl(file, this.locations, this.chain);
|
||||
|
||||
assertEquals(path, resultUrl);
|
||||
String requestPath = "test/bar.css";
|
||||
String version = "123";
|
||||
assertEquals("test/bar-123.css", this.versionStrategy.addVersion(requestPath, version));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ public class CssLinkResourceTransformerTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
|
||||
versionStrategyMap.put("/**", new ContentBasedVersionStrategy());
|
||||
versionStrategyMap.put("/**", new ContentVersionStrategy());
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setVersionStrategyMap(versionStrategyMap);
|
||||
versionResolver.setStrategyMap(versionStrategyMap);
|
||||
|
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||
resolvers.add(versionResolver);
|
||||
|
||||
@@ -15,16 +15,11 @@
|
||||
*/
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.web.servlet.resource.FixedVersionStrategy}
|
||||
* @author Brian Clozel
|
||||
@@ -33,49 +28,36 @@ public class FixedVersionStrategyTests {
|
||||
|
||||
private final String version = "1df341f";
|
||||
|
||||
private final String resourceId = "js/foo.js";
|
||||
private final String path = "js/foo.js";
|
||||
|
||||
private FixedVersionStrategy strategy;
|
||||
|
||||
private FixedVersionStrategy versionStrategy;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.versionStrategy = new FixedVersionStrategy(this.version);
|
||||
this.strategy = new FixedVersionStrategy(this.version);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructWithEmptyPrefixVersion() throws Exception {
|
||||
|
||||
FixedVersionStrategy versionStrategy = new FixedVersionStrategy(" ");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructWithEmptyCallableVersion() throws Exception {
|
||||
|
||||
FixedVersionStrategy versionStrategy = new FixedVersionStrategy(
|
||||
new Callable<String>() {
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return " ";
|
||||
}
|
||||
});
|
||||
public void emptyPrefixVersion() throws Exception {
|
||||
new FixedVersionStrategy(" ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractVersionFromPath() throws Exception {
|
||||
assertEquals(this.version + "/", this.versionStrategy.extractVersionFromPath(this.version + "/" + this.resourceId));
|
||||
assertEquals("", this.versionStrategy.extractVersionFromPath(this.resourceId));
|
||||
public void extractVersion() throws Exception {
|
||||
assertEquals(this.version, this.strategy.extractVersion(this.version + "/" + this.path));
|
||||
assertNull(this.strategy.extractVersion(this.path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteVersionFromPath() throws Exception {
|
||||
assertEquals(this.resourceId,
|
||||
this.versionStrategy.deleteVersionFromPath(this.version + "/" + this.resourceId, this.version + "/"));
|
||||
public void removeVersion() throws Exception {
|
||||
assertEquals("/" + this.path, this.strategy.removeVersion(this.version + "/" + this.path, this.version));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addVersionToUrl() throws Exception {
|
||||
assertEquals(this.version + "/" + this.resourceId,
|
||||
this.versionStrategy.addVersionToUrl(this.resourceId, Collections.<Resource>emptyList(), null));
|
||||
|
||||
public void addVersion() throws Exception {
|
||||
assertEquals(this.version + "/" + this.path, this.strategy.addVersion(this.path, this.version));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ public class GzipResourceResolverTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
|
||||
versionStrategyMap.put("/**", new ContentBasedVersionStrategy());
|
||||
versionStrategyMap.put("/**", new ContentVersionStrategy());
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setVersionStrategyMap(versionStrategyMap);
|
||||
versionResolver.setStrategyMap(versionStrategyMap);
|
||||
|
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||
resolvers.add(new GzipResourceResolver());
|
||||
|
||||
@@ -115,9 +115,9 @@ public class ResourceUrlProviderJavaConfigTests {
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
|
||||
versionStrategyMap.put("/**", new ContentBasedVersionStrategy());
|
||||
versionStrategyMap.put("/**", new ContentVersionStrategy());
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setVersionStrategyMap(versionStrategyMap);
|
||||
versionResolver.setStrategyMap(versionStrategyMap);
|
||||
|
||||
registry.addResourceHandler("/resources/**")
|
||||
.addResourceLocations("classpath:org/springframework/web/servlet/resource/test/")
|
||||
|
||||
@@ -68,9 +68,9 @@ public class ResourceUrlProviderTests {
|
||||
@Test
|
||||
public void getFingerprintedResourceUrl() {
|
||||
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
|
||||
versionStrategyMap.put("/**", new ContentBasedVersionStrategy());
|
||||
versionStrategyMap.put("/**", new ContentVersionStrategy());
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setVersionStrategyMap(versionStrategyMap);
|
||||
versionResolver.setStrategyMap(versionStrategyMap);
|
||||
|
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||
resolvers.add(versionResolver);
|
||||
|
||||
@@ -40,7 +40,7 @@ public class VersionResourceResolverTests {
|
||||
|
||||
private List<Resource> locations;
|
||||
|
||||
private VersionResourceResolver versionResourceResolver;
|
||||
private VersionResourceResolver resolver;
|
||||
|
||||
private ResourceResolverChain chain;
|
||||
|
||||
@@ -49,126 +49,118 @@ public class VersionResourceResolverTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.locations = new ArrayList<Resource>();
|
||||
this.locations = new ArrayList<>();
|
||||
this.locations.add(new ClassPathResource("test/", getClass()));
|
||||
this.locations.add(new ClassPathResource("testalternatepath/", getClass()));
|
||||
|
||||
this.versionResourceResolver = new VersionResourceResolver();
|
||||
this.resolver = new VersionResourceResolver();
|
||||
this.chain = mock(ResourceResolverChain.class);
|
||||
this.versionStrategy = mock(VersionStrategy.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveResourceInternalExisting() throws Exception {
|
||||
public void resolveResourceExisting() throws Exception {
|
||||
String file = "bar.css";
|
||||
Resource expected = new ClassPathResource("test/" + file, getClass());
|
||||
when(this.chain.resolveResource(null, file, this.locations)).thenReturn(expected);
|
||||
|
||||
this.versionResourceResolver
|
||||
.setVersionStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.versionResourceResolver.resolveResourceInternal(null, file, this.locations, this.chain);
|
||||
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver.resolveResourceInternal(null, file, this.locations, this.chain);
|
||||
assertEquals(expected, actual);
|
||||
verify(this.chain, times(1)).resolveResource(null, file, this.locations);
|
||||
verify(this.versionStrategy, never()).extractVersionFromPath(file);
|
||||
verify(this.versionStrategy, never()).extractVersion(file);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveResourceInternalNoStrategy() throws Exception {
|
||||
public void resolveResourceNoVersionStrategy() throws Exception {
|
||||
String file = "missing.css";
|
||||
when(this.chain.resolveResource(null, file, this.locations)).thenReturn(null);
|
||||
|
||||
this.versionResourceResolver.setVersionStrategyMap(Collections.emptyMap());
|
||||
Resource actual = this.versionResourceResolver.resolveResourceInternal(null, file, this.locations, this.chain);
|
||||
this.resolver.setStrategyMap(Collections.emptyMap());
|
||||
Resource actual = this.resolver.resolveResourceInternal(null, file, this.locations, this.chain);
|
||||
assertNull(actual);
|
||||
verify(this.chain, times(1)).resolveResource(null, file, this.locations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveResourceInternalNoCandidateVersion() throws Exception {
|
||||
|
||||
public void resolveResourceNoVersionInPath() throws Exception {
|
||||
String file = "bar.css";
|
||||
when(this.chain.resolveResource(null, file, this.locations)).thenReturn(null);
|
||||
when(this.versionStrategy.extractVersionFromPath(file)).thenReturn("");
|
||||
when(this.versionStrategy.extractVersion(file)).thenReturn("");
|
||||
|
||||
this.versionResourceResolver
|
||||
.setVersionStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.versionResourceResolver.resolveResourceInternal(null, file, this.locations, this.chain);
|
||||
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver.resolveResourceInternal(null, file, this.locations, this.chain);
|
||||
assertNull(actual);
|
||||
verify(this.chain, times(1)).resolveResource(null, file, this.locations);
|
||||
verify(this.versionStrategy, times(1)).extractVersionFromPath(file);
|
||||
verify(this.versionStrategy, times(1)).extractVersion(file);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveResourceInternalNoBaseResource() throws Exception {
|
||||
|
||||
public void resolveResourceNoResourceAfterVersionRemoved() throws Exception {
|
||||
String versionFile = "bar-version.css";
|
||||
String version = "version";
|
||||
String file = "bar.css";
|
||||
when(this.chain.resolveResource(null, versionFile, this.locations)).thenReturn(null);
|
||||
when(this.chain.resolveResource(null, file, this.locations)).thenReturn(null);
|
||||
when(this.versionStrategy.extractVersionFromPath(versionFile)).thenReturn(version);
|
||||
when(this.versionStrategy.deleteVersionFromPath(versionFile, version)).thenReturn(file);
|
||||
when(this.versionStrategy.extractVersion(versionFile)).thenReturn(version);
|
||||
when(this.versionStrategy.removeVersion(versionFile, version)).thenReturn(file);
|
||||
|
||||
this.versionResourceResolver
|
||||
.setVersionStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.versionResourceResolver.resolveResourceInternal(null, versionFile, this.locations, this.chain);
|
||||
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver.resolveResourceInternal(null, versionFile, this.locations, this.chain);
|
||||
assertNull(actual);
|
||||
verify(this.versionStrategy, times(1)).deleteVersionFromPath(versionFile, version);
|
||||
verify(this.versionStrategy, times(1)).removeVersion(versionFile, version);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveResourceInternalNoMatch() throws Exception {
|
||||
|
||||
public void resolveResourceVersionDoesNotMatch() throws Exception {
|
||||
String versionFile = "bar-version.css";
|
||||
String version = "version";
|
||||
String file = "bar.css";
|
||||
Resource expected = new ClassPathResource("test/" + file, getClass());
|
||||
when(this.chain.resolveResource(null, versionFile, this.locations)).thenReturn(null);
|
||||
when(this.chain.resolveResource(null, file, this.locations)).thenReturn(expected);
|
||||
when(this.versionStrategy.extractVersionFromPath(versionFile)).thenReturn(version);
|
||||
when(this.versionStrategy.deleteVersionFromPath(versionFile, version)).thenReturn(file);
|
||||
when(this.versionStrategy.resourceVersionMatches(expected, version)).thenReturn(false);
|
||||
when(this.versionStrategy.extractVersion(versionFile)).thenReturn(version);
|
||||
when(this.versionStrategy.removeVersion(versionFile, version)).thenReturn(file);
|
||||
when(this.versionStrategy.getResourceVersion(expected)).thenReturn("newer-version");
|
||||
|
||||
this.versionResourceResolver
|
||||
.setVersionStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.versionResourceResolver.resolveResourceInternal(null, versionFile, this.locations, this.chain);
|
||||
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver.resolveResourceInternal(null, versionFile, this.locations, this.chain);
|
||||
assertNull(actual);
|
||||
verify(this.versionStrategy, times(1)).resourceVersionMatches(expected, version);
|
||||
verify(this.versionStrategy, times(1)).getResourceVersion(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveResourceInternalMatch() throws Exception {
|
||||
|
||||
public void resolveResourceSuccess() throws Exception {
|
||||
String versionFile = "bar-version.css";
|
||||
String version = "version";
|
||||
String file = "bar.css";
|
||||
Resource expected = new ClassPathResource("test/" + file, getClass());
|
||||
when(this.chain.resolveResource(null, versionFile, this.locations)).thenReturn(null);
|
||||
when(this.chain.resolveResource(null, file, this.locations)).thenReturn(expected);
|
||||
when(this.versionStrategy.extractVersionFromPath(versionFile)).thenReturn(version);
|
||||
when(this.versionStrategy.deleteVersionFromPath(versionFile, version)).thenReturn(file);
|
||||
when(this.versionStrategy.resourceVersionMatches(expected, version)).thenReturn(true);
|
||||
when(this.versionStrategy.extractVersion(versionFile)).thenReturn(version);
|
||||
when(this.versionStrategy.removeVersion(versionFile, version)).thenReturn(file);
|
||||
when(this.versionStrategy.getResourceVersion(expected)).thenReturn(version);
|
||||
|
||||
this.versionResourceResolver
|
||||
.setVersionStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.versionResourceResolver.resolveResourceInternal(null, versionFile, this.locations, this.chain);
|
||||
this.resolver
|
||||
.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver.resolveResourceInternal(null, versionFile, this.locations, this.chain);
|
||||
assertEquals(expected, actual);
|
||||
verify(this.versionStrategy, times(1)).resourceVersionMatches(expected, version);
|
||||
verify(this.versionStrategy, times(1)).getResourceVersion(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findStrategy() throws Exception {
|
||||
public void getStrategyForPath() throws Exception {
|
||||
Map<String, VersionStrategy> strategies = new HashMap<>();
|
||||
VersionStrategy jsStrategy = mock(VersionStrategy.class);
|
||||
VersionStrategy catchAllStrategy = mock(VersionStrategy.class);
|
||||
strategies.put("/**", catchAllStrategy);
|
||||
strategies.put("/**/*.js", jsStrategy);
|
||||
this.versionResourceResolver.setVersionStrategyMap(strategies);
|
||||
this.resolver.setStrategyMap(strategies);
|
||||
|
||||
assertEquals(catchAllStrategy, this.versionResourceResolver.findStrategy("foo.css"));
|
||||
assertEquals(catchAllStrategy, this.versionResourceResolver.findStrategy("foo-js.css"));
|
||||
assertEquals(jsStrategy, this.versionResourceResolver.findStrategy("foo.js"));
|
||||
assertEquals(jsStrategy, this.versionResourceResolver.findStrategy("bar/foo.js"));
|
||||
assertEquals(catchAllStrategy, this.resolver.getStrategyForPath("foo.css"));
|
||||
assertEquals(catchAllStrategy, this.resolver.getStrategyForPath("foo-js.css"));
|
||||
assertEquals(jsStrategy, this.resolver.getStrategyForPath("foo.js"));
|
||||
assertEquals(jsStrategy, this.resolver.getStrategyForPath("bar/foo.js"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user