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
This commit is contained in:
Sam Brannen 2022-09-06 20:07:12 +02:00
parent 1052f48eb6
commit d57e061d3f
3 changed files with 28 additions and 17 deletions

View File

@ -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());
}
}

View File

@ -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 <em>relative path</em> supplied to the constructor.
* <p>If you need the <em>absolute path</em>, use {@link #getAbsolutePath()}
* instead.
* @see #getAbsolutePath()
*/
public final String getPath() {
return this.path;
}
/**
* Return the <em>absolute path</em> 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 + ']';
}

View File

@ -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));
}