From d57e061d3f60010e971337ec426172adeb4fc52b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 6 Sep 2022 20:07:12 +0200 Subject: [PATCH] Introduce getAbsolutePath() in ClassPathResource Since getPath() returns a relative path if the resource was created using the ClassPathResource(String,Class) constructor, there was previously no way to consistently obtain the absolute path to the resource within the class path. This commit addresses this shortcoming by introducing a new getAbsolutePath() for consistently obtaining the absolute path to the resource within the class path. See gh-29083 Closes gh-29094 --- .../aot/hint/ResourceHints.java | 2 +- .../core/io/ClassPathResource.java | 39 +++++++++++++------ .../aot/hint/ResourceHintsTests.java | 4 -- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/aot/hint/ResourceHints.java b/spring-core/src/main/java/org/springframework/aot/hint/ResourceHints.java index 4bdf8a89c04..82be1949420 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/ResourceHints.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/ResourceHints.java @@ -121,7 +121,7 @@ public class ResourceHints { */ public void registerResourceIfNecessary(Resource resource) { if (resource instanceof ClassPathResource classPathResource && classPathResource.exists()) { - registerPattern(classPathResource.getPath()); + registerPattern(classPathResource.getAbsolutePath()); } } diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java index 93b5a503756..79d6fea794a 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -47,6 +47,8 @@ public class ClassPathResource extends AbstractFileResolvingResource { private final String path; + private final String absolutePath; + @Nullable private final ClassLoader classLoader; @@ -83,6 +85,7 @@ public class ClassPathResource extends AbstractFileResolvingResource { pathToUse = pathToUse.substring(1); } this.path = pathToUse; + this.absolutePath = pathToUse; this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); this.clazz = null; } @@ -100,6 +103,16 @@ public class ClassPathResource extends AbstractFileResolvingResource { public ClassPathResource(String path, @Nullable Class clazz) { Assert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); + + String absolutePath = this.path; + if (clazz != null && !absolutePath.startsWith("/")) { + absolutePath = ClassUtils.classPackageAsResourcePath(clazz) + "/" + absolutePath; + } + else if (absolutePath.startsWith("/")) { + absolutePath = absolutePath.substring(1); + } + this.absolutePath = absolutePath; + this.classLoader = null; this.clazz = clazz; } @@ -117,11 +130,24 @@ public class ClassPathResource extends AbstractFileResolvingResource { * {@link ClassPathResource#ClassPathResource(String, Class)}, the * returned path is a {@linkplain StringUtils#cleanPath(String) cleaned} * version of the relative path supplied to the constructor. + *

If you need the absolute path, use {@link #getAbsolutePath()} + * instead. + * @see #getAbsolutePath() */ public final String getPath() { return this.path; } + /** + * Return the absolute path for this resource, as a resource path + * within the class path. + * @since 6.0 + * @see #getPath() + */ + public final String getAbsolutePath() { + return this.absolutePath; + } + /** * Return the {@link ClassLoader} that this resource will be obtained from. */ @@ -245,18 +271,7 @@ public class ClassPathResource extends AbstractFileResolvingResource { */ @Override public String getDescription() { - StringBuilder builder = new StringBuilder("class path resource ["); - String pathToUse = this.path; - if (this.clazz != null && !pathToUse.startsWith("/")) { - builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); - builder.append('/'); - } - if (pathToUse.startsWith("/")) { - pathToUse = pathToUse.substring(1); - } - builder.append(pathToUse); - builder.append(']'); - return builder.toString(); + return "class path resource [" + this.absolutePath + ']'; } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java index 3b00278fe47..690c8f6d575 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java @@ -21,7 +21,6 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.ResourceHintsTests.Nested.Inner; @@ -136,14 +135,11 @@ class ResourceHintsTests { assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(patternOf(path)); } - @Disabled("Disabled since ClassPathResource.getPath() does not honor its contract for relative resources") @Test void registerResourceIfNecessaryWithExistingRelativeClassPathResource() { String path = "org/springframework/aot/hint/support"; ClassPathResource resource = new ClassPathResource("support", RuntimeHints.class); this.resourceHints.registerResourceIfNecessary(resource); - // This unfortunately fails since ClassPathResource.getPath() returns - // "support" instead of "org/springframework/aot/hint/support". assertThat(this.resourceHints.resourcePatterns()).singleElement().satisfies(patternOf(path)); }