Skip to content

Commit 57221ab

Browse files
committed
ContentVersionStrategy has non-blocking contract
Issue: SPR-15773
1 parent efb5ba5 commit 57221ab

File tree

7 files changed

+60
-48
lines changed

7 files changed

+60
-48
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractPrefixVersionStrategy.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ protected AbstractPrefixVersionStrategy(String version) {
4343
}
4444

4545

46-
public String getPrefix() {
47-
return this.prefix;
48-
}
49-
50-
5146
@Override
5247
public String extractVersion(String requestPath) {
5348
return requestPath.startsWith(this.prefix) ? this.prefix : null;

spring-webflux/src/main/java/org/springframework/web/reactive/resource/ContentVersionStrategy.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@
1616

1717
package org.springframework.web.reactive.resource;
1818

19-
import java.io.IOException;
19+
import reactor.core.publisher.Mono;
2020

2121
import org.springframework.core.io.Resource;
22+
import org.springframework.core.io.buffer.DataBuffer;
23+
import org.springframework.core.io.buffer.DataBufferFactory;
24+
import org.springframework.core.io.buffer.DataBufferUtils;
25+
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
2226
import org.springframework.util.DigestUtils;
27+
import org.springframework.util.StreamUtils;
2328

2429
/**
2530
* A {@code VersionStrategy} that calculates an Hex MD5 hashes from the content
@@ -33,15 +38,19 @@
3338
*/
3439
public class ContentVersionStrategy extends AbstractFileNameVersionStrategy {
3540

41+
private static final DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
42+
3643

3744
@Override
38-
public String getResourceVersion(Resource resource) {
39-
try {
40-
return DigestUtils.md5DigestAsHex(resource.getInputStream());
41-
}
42-
catch (IOException ex) {
43-
throw new IllegalStateException("Failed to calculate hash for " + resource, ex);
44-
}
45+
public Mono<String> getResourceVersion(Resource resource) {
46+
return DataBufferUtils.read(resource, dataBufferFactory, StreamUtils.BUFFER_SIZE)
47+
.reduce(DataBuffer::write)
48+
.map(buffer -> {
49+
byte[] result = new byte[buffer.readableByteCount()];
50+
buffer.read(result);
51+
DataBufferUtils.release(buffer);
52+
return DigestUtils.md5DigestAsHex(result);
53+
});
4554
}
4655

4756
}

spring-webflux/src/main/java/org/springframework/web/reactive/resource/FixedVersionStrategy.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.web.reactive.resource;
1818

19+
import reactor.core.publisher.Mono;
20+
1921
import org.springframework.core.io.Resource;
2022

2123
/**
@@ -33,19 +35,22 @@
3335
*/
3436
public class FixedVersionStrategy extends AbstractPrefixVersionStrategy {
3537

38+
private final Mono<String> versionMono;
39+
3640

3741
/**
3842
* Create a new FixedVersionStrategy with the given version string.
3943
* @param version the fixed version string to use
4044
*/
4145
public FixedVersionStrategy(String version) {
4246
super(version);
47+
this.versionMono = Mono.just(version);
4348
}
4449

4550

4651
@Override
47-
public String getResourceVersion(Resource resource) {
48-
return getPrefix();
52+
public Mono<String> getResourceVersion(Resource resource) {
53+
return this.versionMono;
4954
}
5055

5156
}

spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -172,36 +172,37 @@ private Mono<Resource> resolveVersionedResource(@Nullable ServerWebExchange exch
172172
return Mono.empty();
173173
}
174174

175-
String candidateVersion = versionStrategy.extractVersion(requestPath);
176-
if (StringUtils.isEmpty(candidateVersion)) {
175+
String candidate = versionStrategy.extractVersion(requestPath);
176+
if (StringUtils.isEmpty(candidate)) {
177177
if (logger.isTraceEnabled()) {
178178
logger.trace("No version found in path \"" + requestPath + "\"");
179179
}
180180
return Mono.empty();
181181
}
182182

183-
String simplePath = versionStrategy.removeVersion(requestPath, candidateVersion);
183+
String simplePath = versionStrategy.removeVersion(requestPath, candidate);
184184
if (logger.isTraceEnabled()) {
185185
logger.trace("Extracted version from path, re-resolving without version: \"" + simplePath + "\"");
186186
}
187187

188188
return chain.resolveResource(exchange, simplePath, locations)
189-
.flatMap(baseResource -> {
190-
String actualVersion = versionStrategy.getResourceVersion(baseResource);
191-
if (candidateVersion.equals(actualVersion)) {
192-
if (logger.isTraceEnabled()) {
193-
logger.trace("Resource matches extracted version [" + candidateVersion + "]");
194-
}
195-
return Mono.just(new FileNameVersionedResource(baseResource, candidateVersion));
196-
}
197-
else {
198-
if (logger.isTraceEnabled()) {
199-
logger.trace("Potential resource found for \"" + requestPath + "\", but version [" +
200-
candidateVersion + "] does not match");
201-
}
202-
return Mono.empty();
203-
}
204-
});
189+
.filterWhen(resource -> versionStrategy.getResourceVersion(resource)
190+
.map(actual -> {
191+
if (candidate.equals(actual)) {
192+
if (logger.isTraceEnabled()) {
193+
logger.trace("Resource matches extracted version [" + candidate + "]");
194+
}
195+
return true;
196+
}
197+
else {
198+
if (logger.isTraceEnabled()) {
199+
logger.trace("Potential resource found for \"" + requestPath + "\", " +
200+
"but version [" + candidate + "] does not match");
201+
}
202+
return false;
203+
}
204+
}))
205+
.map(resource -> new FileNameVersionedResource(resource, candidate));
205206
}
206207

207208
@Override
@@ -211,22 +212,22 @@ protected Mono<String> resolveUrlPathInternal(String resourceUrlPath,
211212
return chain.resolveUrlPath(resourceUrlPath, locations)
212213
.flatMap(baseUrl -> {
213214
if (StringUtils.hasText(baseUrl)) {
214-
VersionStrategy versionStrategy = getStrategyForPath(resourceUrlPath);
215-
if (versionStrategy == null) {
215+
VersionStrategy strategy = getStrategyForPath(resourceUrlPath);
216+
if (strategy == null) {
216217
return Mono.just(baseUrl);
217218
}
218219
if (logger.isTraceEnabled()) {
219220
logger.trace("Getting the original resource to determine version " +
220221
"for path \"" + resourceUrlPath + "\"");
221222
}
222223
return chain.resolveResource(null, baseUrl, locations)
223-
.map(resource -> {
224-
String version = versionStrategy.getResourceVersion(resource);
225-
if (logger.isTraceEnabled()) {
226-
logger.trace("Determined version [" + version + "] for " + resource);
227-
}
228-
return versionStrategy.addVersion(baseUrl, version);
229-
});
224+
.flatMap(resource -> strategy.getResourceVersion(resource)
225+
.map(version -> {
226+
if (logger.isTraceEnabled()) {
227+
logger.trace("Determined version [" + version + "] for " + resource);
228+
}
229+
return strategy.addVersion(baseUrl, version);
230+
}));
230231
}
231232
return Mono.empty();
232233
});

spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionStrategy.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.web.reactive.resource;
1818

19+
import reactor.core.publisher.Mono;
20+
1921
import org.springframework.core.io.Resource;
2022
import org.springframework.lang.Nullable;
2123

@@ -58,8 +60,8 @@ public interface VersionStrategy {
5860
/**
5961
* Determine the version for the given resource.
6062
* @param resource the resource to check
61-
* @return the version (never {@code null})
63+
* @return the resource version
6264
*/
63-
String getResourceVersion(Resource resource);
65+
Mono<String> getResourceVersion(Resource resource);
6466

6567
}

spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void removeVersion() throws Exception {
6666
public void getResourceVersion() throws Exception {
6767
Resource expected = new ClassPathResource("test/bar.css", getClass());
6868
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream()));
69-
assertEquals(hash, this.strategy.getResourceVersion(expected));
69+
assertEquals(hash, this.strategy.getResourceVersion(expected).block());
7070
}
7171

7272
@Test

spring-webflux/src/test/java/org/springframework/web/reactive/resource/VersionResourceResolverTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public void resolveResourceVersionDoesNotMatch() throws Exception {
145145
given(this.chain.resolveResource(null, file, this.locations)).willReturn(Mono.just(expected));
146146
given(this.versionStrategy.extractVersion(versionFile)).willReturn(version);
147147
given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file);
148-
given(this.versionStrategy.getResourceVersion(expected)).willReturn("newer-version");
148+
given(this.versionStrategy.getResourceVersion(expected)).willReturn(Mono.just("newer-version"));
149149

150150
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
151151
Resource actual = this.resolver
@@ -167,7 +167,7 @@ public void resolveResourceSuccess() throws Exception {
167167
given(this.chain.resolveResource(exchange, file, this.locations)).willReturn(Mono.just(expected));
168168
given(this.versionStrategy.extractVersion(versionFile)).willReturn(version);
169169
given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file);
170-
given(this.versionStrategy.getResourceVersion(expected)).willReturn(version);
170+
given(this.versionStrategy.getResourceVersion(expected)).willReturn(Mono.just(version));
171171

172172
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
173173
Resource actual = this.resolver

0 commit comments

Comments
 (0)