Skip to content

Commit d917b8a

Browse files
rstoyanchevbclozel
authored andcommitted
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.
1 parent 9f3c1cf commit d917b8a

15 files changed

+303
-342
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractVersionStrategy.java

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.apache.commons.logging.Log;
77
import org.apache.commons.logging.LogFactory;
88

9+
import org.springframework.util.Assert;
910
import org.springframework.util.StringUtils;
1011

1112
/**
@@ -20,73 +21,106 @@
2021
* version string.
2122
*
2223
* @author Brian Clozel
24+
* @author Rossen Stoyanchev
2325
* @since 4.1
2426
*/
2527
public abstract class AbstractVersionStrategy implements VersionStrategy {
2628

27-
private static final Pattern pattern = Pattern.compile("-(\\S*)\\.");
28-
2929
protected final Log logger = LogFactory.getLog(getClass());
3030

31-
/**
32-
* Extracts a version string from the request path, as a suffix in the resource
33-
* file name.
34-
* @param requestPath the request path to extract the version string from
35-
* @return a version string or an empty string if none was found
36-
*/
37-
protected String extractVersionFromFilename(String requestPath) {
38-
Matcher matcher = pattern.matcher(requestPath);
39-
if (matcher.find()) {
40-
String match = matcher.group(1);
41-
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match;
42-
}
43-
else {
44-
return "";
45-
}
31+
private final VersionPathStrategy pathStrategy;
32+
33+
34+
protected AbstractVersionStrategy(VersionPathStrategy pathStrategy) {
35+
Assert.notNull(pathStrategy, "'pathStrategy' is required");
36+
this.pathStrategy = pathStrategy;
4637
}
4738

48-
/**
49-
* Deletes the given candidate version string from the request path.
50-
* The version string should be a suffix in the resource file name.
51-
*/
52-
protected String deleteVersionFromFilename(String requestPath, String candidateVersion) {
53-
return StringUtils.delete(requestPath, "-" + candidateVersion);
39+
40+
public VersionPathStrategy getVersionPathStrategy() {
41+
return this.pathStrategy;
5442
}
5543

56-
/**
57-
* Adds the given version string to the baseUrl, as a file name suffix.
58-
*/
59-
protected String addVersionToFilename(String baseUrl, String version) {
60-
String baseFilename = StringUtils.stripFilenameExtension(baseUrl);
61-
String extension = StringUtils.getFilenameExtension(baseUrl);
62-
return baseFilename + "-" + version + "." + extension;
44+
45+
@Override
46+
public String extractVersion(String requestPath) {
47+
return this.pathStrategy.extractVersion(requestPath);
6348
}
6449

65-
/**
66-
* Extracts a version string from the request path, as a prefix in the request path.
67-
* @param requestPath the request path to extract the version string from
68-
* @return a version string or an empty string if none was found
69-
*/
70-
protected String extractVersionAsPrefix(String requestPath, String prefix) {
71-
if (requestPath.startsWith(prefix)) {
72-
return prefix;
73-
}
74-
return "";
50+
@Override
51+
public String removeVersion(String requestPath, String version) {
52+
return this.pathStrategy.removeVersion(requestPath, version);
53+
}
54+
55+
@Override
56+
public String addVersion(String requestPath, String version) {
57+
return this.pathStrategy.addVersion(requestPath, version);
7558
}
7659

60+
7761
/**
78-
* Deletes the given candidate version string from the request path.
79-
* The version string should be a prefix in the request path.
62+
* A prefix-based {@code VersionPathStrategy},
63+
* e.g. {@code "{version}/path/foo.js"}.
8064
*/
81-
protected String deleteVersionAsPrefix(String requestPath, String version) {
82-
return requestPath.substring(version.length());
65+
protected static class PrefixVersionPathStrategy implements VersionPathStrategy {
66+
67+
private final String prefix;
68+
69+
70+
public PrefixVersionPathStrategy(String version) {
71+
Assert.hasText(version, "'version' is required and must not be empty");
72+
this.prefix = version;
73+
}
74+
75+
@Override
76+
public String extractVersion(String requestPath) {
77+
return (requestPath.startsWith(this.prefix) ? this.prefix : null);
78+
}
79+
80+
@Override
81+
public String removeVersion(String requestPath, String version) {
82+
return requestPath.substring(this.prefix.length());
83+
}
84+
85+
@Override
86+
public String addVersion(String path, String version) {
87+
return (this.prefix.endsWith("/") || path.startsWith("/") ? this.prefix + path : this.prefix + "/" + path);
88+
}
89+
8390
}
8491

8592
/**
86-
* Adds the given version string to the baseUrl, as a prefix in the request path.
93+
* File name-based {@code VersionPathStrategy},
94+
* e.g. {@code "path/foo-{version}.css"}.
8795
*/
88-
protected String addVersionAsPrefix(String baseUrl, String version) {
89-
return version + baseUrl;
96+
protected static class FileNameVersionPathStrategy implements VersionPathStrategy {
97+
98+
private static final Pattern pattern = Pattern.compile("-(\\S*)\\.");
99+
100+
101+
@Override
102+
public String extractVersion(String requestPath) {
103+
Matcher matcher = pattern.matcher(requestPath);
104+
if (matcher.find()) {
105+
String match = matcher.group(1);
106+
return match.contains("-") ? match.substring(match.lastIndexOf("-") + 1) : match;
107+
}
108+
else {
109+
return null;
110+
}
111+
}
112+
113+
@Override
114+
public String removeVersion(String requestPath, String version) {
115+
return StringUtils.delete(requestPath, "-" + version);
116+
}
117+
118+
@Override
119+
public String addVersion(String requestPath, String version) {
120+
String baseFilename = StringUtils.stripFilenameExtension(requestPath);
121+
String extension = StringUtils.getFilenameExtension(requestPath);
122+
return baseFilename + "-" + version + "." + extension;
123+
}
90124
}
91125

92126
}

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ContentBasedVersionStrategy.java

Lines changed: 0 additions & 81 deletions
This file was deleted.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2002-2014 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+
package org.springframework.web.servlet.resource;
17+
18+
import java.io.IOException;
19+
20+
import org.springframework.core.io.Resource;
21+
import org.springframework.util.DigestUtils;
22+
import org.springframework.util.FileCopyUtils;
23+
24+
/**
25+
* A {@code VersionStrategy} that calculates an Hex MD5 hashes from the content
26+
* of the resource and appends it to the file name, e.g.
27+
* {@code "styles/main-e36d2e05253c6c7085a91522ce43a0b4.css"}.
28+
*
29+
* @author Brian Clozel
30+
* @author Rossen Stoyanchev
31+
* @since 4.1
32+
* @see VersionResourceResolver
33+
*/
34+
public class ContentVersionStrategy extends AbstractVersionStrategy {
35+
36+
37+
public ContentVersionStrategy() {
38+
super(new FileNameVersionPathStrategy());
39+
}
40+
41+
42+
@Override
43+
public String getResourceVersion(Resource resource) {
44+
try {
45+
byte[] content = FileCopyUtils.copyToByteArray(resource.getInputStream());
46+
return DigestUtils.md5DigestAsHex(content);
47+
}
48+
catch (IOException ex) {
49+
throw new IllegalStateException("Failed to calculate hash for resource [" + resource + "]", ex);
50+
}
51+
}
52+
53+
}

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/FixedVersionStrategy.java

Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,18 @@
1515
*/
1616
package org.springframework.web.servlet.resource;
1717

18-
import java.util.List;
19-
import java.util.concurrent.Callable;
20-
2118
import org.springframework.core.io.Resource;
22-
import org.springframework.util.Assert;
2319

2420
/**
25-
* A {@code VersionStrategy} that handles unique, static, application-wide version strings
26-
* as prefixes in the request path.
27-
*
28-
* <p>Enables inserting a unique and static version String (e.g. reduced SHA, version name,
29-
* release date) at the beginning of resource paths so that when a new version of the application
30-
* is released, clients are forced to reload application resources.
21+
* A {@code VersionStrategy} that relies on a fixed version applied as a request
22+
* path prefix, e.g. reduced SHA, version name, release date, etc.
3123
*
32-
* <p>This is useful when changing resource names is not an option (e.g. when
33-
* using JavaScript module loaders). If that's not the case, the use of
34-
* {@link ContentBasedVersionStrategy} provides more optimal performance since
35-
* version is generated on a per-resource basis: only actually modified resources are reloaded
36-
* by the client.
24+
* <p>This is useful for example when {@link ContentVersionStrategy} cannot be
25+
* used such as when using JavaScript module loaders which are in charge of
26+
* loading the JavaScript resources and need to know their relative paths.
3727
*
3828
* @author Brian Clozel
39-
* @author Sam Brannen
29+
* @author Rossen Stoyanchev
4030
* @since 4.1
4131
* @see VersionResourceResolver
4232
*/
@@ -46,44 +36,17 @@ public class FixedVersionStrategy extends AbstractVersionStrategy {
4636

4737
/**
4838
* Create a new FixedVersionStrategy with the given version string.
49-
* @param fixedVersion the fixed version string to use
50-
*/
51-
public FixedVersionStrategy(String fixedVersion) {
52-
Assert.hasText(fixedVersion, "version must not be null or empty");
53-
this.version = fixedVersion.endsWith("/") ? fixedVersion : fixedVersion + "/";
54-
}
55-
56-
/**
57-
* Create a new FixedVersionStrategy and get the version string to use by
58-
* calling the given {@code Callable} instance.
39+
* @param version the fixed version string to use
5940
*/
60-
public FixedVersionStrategy(Callable<String> versionInitializer) throws Exception {
61-
String fixedVersion = versionInitializer.call();
62-
Assert.hasText(fixedVersion, "version must not be null or empty");
63-
this.version = fixedVersion.endsWith("/") ? fixedVersion : fixedVersion + "/";
64-
}
65-
66-
@Override
67-
public String extractVersionFromPath(String requestPath) {
68-
69-
return extractVersionAsPrefix(requestPath, this.version);
70-
}
71-
72-
@Override
73-
public String deleteVersionFromPath(String requestPath, String candidateVersion) {
74-
return deleteVersionAsPrefix(requestPath, candidateVersion);
41+
public FixedVersionStrategy(String version) {
42+
super(new PrefixVersionPathStrategy(version));
43+
this.version = version;
7544
}
7645

77-
@Override
78-
public boolean resourceVersionMatches(Resource baseResource, String candidateVersion) {
79-
return this.version.equals(candidateVersion);
80-
}
8146

8247
@Override
83-
public String addVersionToUrl(String baseUrl, List<? extends Resource> locations,
84-
ResourceResolverChain chain) {
85-
86-
return addVersionAsPrefix(baseUrl, this.version);
48+
public String getResourceVersion(Resource resource) {
49+
return this.version;
8750
}
8851

8952
}

0 commit comments

Comments
 (0)