diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractPrefixVersionStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractPrefixVersionStrategy.java index 906b7be249..75d7e541a9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractPrefixVersionStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractPrefixVersionStrategy.java @@ -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; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ContentVersionStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ContentVersionStrategy.java index 9213209273..ab0dc1cb9b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ContentVersionStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ContentVersionStrategy.java @@ -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 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); + }); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/FixedVersionStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/FixedVersionStrategy.java index f16deda667..d7a2e02333 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/FixedVersionStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/FixedVersionStrategy.java @@ -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 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 getResourceVersion(Resource resource) { + return this.versionMono; } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java index a96d51432b..256f8270d7 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java @@ -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(); }); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionStrategy.java index bc8ed43556..7c5bb95051 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionStrategy.java @@ -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 getResourceVersion(Resource resource); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java index bfb8cdf36c..0e6dbd16e5 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java @@ -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 diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/VersionResourceResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/VersionResourceResolverTests.java index 3163632673..4d5896893d 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/resource/VersionResourceResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/resource/VersionResourceResolverTests.java @@ -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