ContentVersionStrategy has non-blocking contract
Issue: SPR-15773
This commit is contained in:
parent
efb5ba5a95
commit
57221ab15c
|
@ -43,11 +43,6 @@ public abstract class AbstractPrefixVersionStrategy implements VersionStrategy {
|
|||
}
|
||||
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String extractVersion(String requestPath) {
|
||||
return requestPath.startsWith(this.prefix) ? this.prefix : null;
|
||||
|
|
|
@ -16,10 +16,15 @@
|
|||
|
||||
package org.springframework.web.reactive.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* A {@code VersionStrategy} that calculates an Hex MD5 hashes from the content
|
||||
|
@ -33,15 +38,19 @@ import org.springframework.util.DigestUtils;
|
|||
*/
|
||||
public class ContentVersionStrategy extends AbstractFileNameVersionStrategy {
|
||||
|
||||
private static final DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
|
||||
|
||||
|
||||
@Override
|
||||
public String getResourceVersion(Resource resource) {
|
||||
try {
|
||||
return DigestUtils.md5DigestAsHex(resource.getInputStream());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to calculate hash for " + resource, ex);
|
||||
}
|
||||
public Mono<String> getResourceVersion(Resource resource) {
|
||||
return DataBufferUtils.read(resource, dataBufferFactory, StreamUtils.BUFFER_SIZE)
|
||||
.reduce(DataBuffer::write)
|
||||
.map(buffer -> {
|
||||
byte[] result = new byte[buffer.readableByteCount()];
|
||||
buffer.read(result);
|
||||
DataBufferUtils.release(buffer);
|
||||
return DigestUtils.md5DigestAsHex(result);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.web.reactive.resource;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
|
@ -33,6 +35,8 @@ import org.springframework.core.io.Resource;
|
|||
*/
|
||||
public class FixedVersionStrategy extends AbstractPrefixVersionStrategy {
|
||||
|
||||
private final Mono<String> versionMono;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new FixedVersionStrategy with the given version string.
|
||||
|
@ -40,12 +44,13 @@ public class FixedVersionStrategy extends AbstractPrefixVersionStrategy {
|
|||
*/
|
||||
public FixedVersionStrategy(String version) {
|
||||
super(version);
|
||||
this.versionMono = Mono.just(version);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getResourceVersion(Resource resource) {
|
||||
return getPrefix();
|
||||
public Mono<String> getResourceVersion(Resource resource) {
|
||||
return this.versionMono;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -172,36 +172,37 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
|||
return Mono.empty();
|
||||
}
|
||||
|
||||
String candidateVersion = versionStrategy.extractVersion(requestPath);
|
||||
if (StringUtils.isEmpty(candidateVersion)) {
|
||||
String candidate = versionStrategy.extractVersion(requestPath);
|
||||
if (StringUtils.isEmpty(candidate)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No version found in path \"" + requestPath + "\"");
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String simplePath = versionStrategy.removeVersion(requestPath, candidateVersion);
|
||||
String simplePath = versionStrategy.removeVersion(requestPath, candidate);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Extracted version from path, re-resolving without version: \"" + simplePath + "\"");
|
||||
}
|
||||
|
||||
return chain.resolveResource(exchange, simplePath, locations)
|
||||
.flatMap(baseResource -> {
|
||||
String actualVersion = versionStrategy.getResourceVersion(baseResource);
|
||||
if (candidateVersion.equals(actualVersion)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resource matches extracted version [" + candidateVersion + "]");
|
||||
}
|
||||
return Mono.just(new FileNameVersionedResource(baseResource, candidateVersion));
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Potential resource found for \"" + requestPath + "\", but version [" +
|
||||
candidateVersion + "] does not match");
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
});
|
||||
.filterWhen(resource -> versionStrategy.getResourceVersion(resource)
|
||||
.map(actual -> {
|
||||
if (candidate.equals(actual)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resource matches extracted version [" + candidate + "]");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Potential resource found for \"" + requestPath + "\", " +
|
||||
"but version [" + candidate + "] does not match");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}))
|
||||
.map(resource -> new FileNameVersionedResource(resource, candidate));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,8 +212,8 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
|||
return chain.resolveUrlPath(resourceUrlPath, locations)
|
||||
.flatMap(baseUrl -> {
|
||||
if (StringUtils.hasText(baseUrl)) {
|
||||
VersionStrategy versionStrategy = getStrategyForPath(resourceUrlPath);
|
||||
if (versionStrategy == null) {
|
||||
VersionStrategy strategy = getStrategyForPath(resourceUrlPath);
|
||||
if (strategy == null) {
|
||||
return Mono.just(baseUrl);
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
|
@ -220,13 +221,13 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
|||
"for path \"" + resourceUrlPath + "\"");
|
||||
}
|
||||
return chain.resolveResource(null, baseUrl, locations)
|
||||
.map(resource -> {
|
||||
String version = versionStrategy.getResourceVersion(resource);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Determined version [" + version + "] for " + resource);
|
||||
}
|
||||
return versionStrategy.addVersion(baseUrl, version);
|
||||
});
|
||||
.flatMap(resource -> strategy.getResourceVersion(resource)
|
||||
.map(version -> {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Determined version [" + version + "] for " + resource);
|
||||
}
|
||||
return strategy.addVersion(baseUrl, version);
|
||||
}));
|
||||
}
|
||||
return Mono.empty();
|
||||
});
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.web.reactive.resource;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
|
@ -58,8 +60,8 @@ public interface VersionStrategy {
|
|||
/**
|
||||
* Determine the version for the given resource.
|
||||
* @param resource the resource to check
|
||||
* @return the version (never {@code null})
|
||||
* @return the resource version
|
||||
*/
|
||||
String getResourceVersion(Resource resource);
|
||||
Mono<String> getResourceVersion(Resource resource);
|
||||
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class ContentBasedVersionStrategyTests {
|
|||
public void getResourceVersion() throws Exception {
|
||||
Resource expected = new ClassPathResource("test/bar.css", getClass());
|
||||
String hash = DigestUtils.md5DigestAsHex(FileCopyUtils.copyToByteArray(expected.getInputStream()));
|
||||
assertEquals(hash, this.strategy.getResourceVersion(expected));
|
||||
assertEquals(hash, this.strategy.getResourceVersion(expected).block());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -145,7 +145,7 @@ public class VersionResourceResolverTests {
|
|||
given(this.chain.resolveResource(null, file, this.locations)).willReturn(Mono.just(expected));
|
||||
given(this.versionStrategy.extractVersion(versionFile)).willReturn(version);
|
||||
given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file);
|
||||
given(this.versionStrategy.getResourceVersion(expected)).willReturn("newer-version");
|
||||
given(this.versionStrategy.getResourceVersion(expected)).willReturn(Mono.just("newer-version"));
|
||||
|
||||
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver
|
||||
|
@ -167,7 +167,7 @@ public class VersionResourceResolverTests {
|
|||
given(this.chain.resolveResource(exchange, file, this.locations)).willReturn(Mono.just(expected));
|
||||
given(this.versionStrategy.extractVersion(versionFile)).willReturn(version);
|
||||
given(this.versionStrategy.removeVersion(versionFile, version)).willReturn(file);
|
||||
given(this.versionStrategy.getResourceVersion(expected)).willReturn(version);
|
||||
given(this.versionStrategy.getResourceVersion(expected)).willReturn(Mono.just(version));
|
||||
|
||||
this.resolver.setStrategyMap(Collections.singletonMap("/**", this.versionStrategy));
|
||||
Resource actual = this.resolver
|
||||
|
|
Loading…
Reference in New Issue