Add working directory support for ApplicationResourceLoader
See gh-41137
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.boot.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
@@ -40,6 +45,7 @@ import org.springframework.util.StringUtils;
|
||||
* {@code DefaultResourceLoader}, which resolves unqualified paths to classpath resources.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
* @since 3.3.0
|
||||
*/
|
||||
@@ -109,7 +115,23 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public static ResourceLoader get(ClassLoader classLoader, SpringFactoriesLoader springFactoriesLoader) {
|
||||
return get(ApplicationFileSystemResourceLoader.get(classLoader), springFactoriesLoader);
|
||||
return get(classLoader, springFactoriesLoader, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResourceLoader} supporting additional {@link ProtocolResolver
|
||||
* ProtocolResolvers} registered in {@code spring.factories}.
|
||||
* @param classLoader the class loader to use or {@code null} to use the default class
|
||||
* loader
|
||||
* @param springFactoriesLoader the {@link SpringFactoriesLoader} used to load
|
||||
* {@link ProtocolResolver ProtocolResolvers}
|
||||
* @param workingDirectory the working directory
|
||||
* @return a {@link ResourceLoader} instance
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public static ResourceLoader get(ClassLoader classLoader, SpringFactoriesLoader springFactoriesLoader,
|
||||
Path workingDirectory) {
|
||||
return get(ApplicationFileSystemResourceLoader.get(classLoader, workingDirectory), springFactoriesLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,10 +192,41 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
||||
*/
|
||||
private static final class ApplicationFileSystemResourceLoader extends DefaultResourceLoader {
|
||||
|
||||
private static final ResourceLoader shared = new ApplicationFileSystemResourceLoader(null);
|
||||
private static final ResourceLoader shared = new ApplicationFileSystemResourceLoader(null, null);
|
||||
|
||||
private ApplicationFileSystemResourceLoader(ClassLoader classLoader) {
|
||||
private final Path workingDirectory;
|
||||
|
||||
private ApplicationFileSystemResourceLoader(ClassLoader classLoader, Path workingDirectory) {
|
||||
super(classLoader);
|
||||
this.workingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource(String location) {
|
||||
Resource resource = super.getResource(location);
|
||||
if (this.workingDirectory == null) {
|
||||
return resource;
|
||||
}
|
||||
if (!resource.isFile()) {
|
||||
return resource;
|
||||
}
|
||||
return resolveFile(resource);
|
||||
}
|
||||
|
||||
private Resource resolveFile(Resource resource) {
|
||||
try {
|
||||
File file = resource.getFile();
|
||||
if (file.isAbsolute()) {
|
||||
return resource;
|
||||
}
|
||||
return new ApplicationResource(new File(this.workingDirectory.toFile(), file.getPath()).getPath());
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
return resource;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -181,8 +234,12 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
||||
return new ApplicationResource(path);
|
||||
}
|
||||
|
||||
static ResourceLoader get(ClassLoader classLoader) {
|
||||
return (classLoader != null) ? new ApplicationFileSystemResourceLoader(classLoader)
|
||||
static ResourceLoader get(ClassLoader classLoader, Path workingDirectory) {
|
||||
if (classLoader == null && workingDirectory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"It's not possible to use null as 'classLoader' but specify a 'workingDirectory'");
|
||||
}
|
||||
return (classLoader != null) ? new ApplicationFileSystemResourceLoader(classLoader, workingDirectory)
|
||||
: ApplicationFileSystemResourceLoader.shared;
|
||||
}
|
||||
|
||||
@@ -218,7 +275,7 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
||||
|
||||
private final boolean preferFileResolution;
|
||||
|
||||
private Class<?> servletContextResourceClass;
|
||||
private final Class<?> servletContextResourceClass;
|
||||
|
||||
ProtocolResolvingResourceLoader(ResourceLoader resourceLoader, List<ProtocolResolver> protocolResolvers,
|
||||
boolean preferFileResolution) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Base64;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.UnaryOperator;
|
||||
@@ -45,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Tests for {@link ApplicationResourceLoader}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class ApplicationResourceLoaderTests {
|
||||
|
||||
@@ -61,6 +63,121 @@ class ApplicationResourceLoaderTests {
|
||||
assertThat(contentAsString(resource)).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadAbsolutePath() throws IOException {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource("/root/file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/root").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadAbsolutePathWithWorkingDirectory() throws IOException {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("/root/file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/root").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadRelativeFilename() throws IOException {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource("file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasNoParent().hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadRelativeFilenameWithWorkingDirectory() throws IOException {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/working-directory").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadRelativePathWithWorkingDirectory() throws IOException {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("a/file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/working-directory/a").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadClasspathLocations() {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource("classpath:a-file");
|
||||
assertThat(resource.exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadNonExistentClasspathLocations() {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource("classpath:doesnt-exist");
|
||||
assertThat(resource.exists()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadClasspathLocationsWithWorkingDirectory() {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("classpath:a-file");
|
||||
assertThat(resource.exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadNonExistentClasspathLocationsWithWorkingDirectory() {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("classpath:doesnt-exist");
|
||||
assertThat(resource.exists()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadRelativeFileUris() throws IOException {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource("file:file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasNoParent().hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadAbsoluteFileUris() throws IOException {
|
||||
Resource resource = ApplicationResourceLoader.get().getResource("file:/file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadRelativeFileUrisWithWorkingDirectory() throws IOException {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("file:file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/working-directory").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadAbsoluteFileUrisWithWorkingDirectory() throws IOException {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
Resource resource = ApplicationResourceLoader
|
||||
.get(classLoader, SpringFactoriesLoader.forDefaultResourceLocation(classLoader),
|
||||
Path.of("/working-directory"))
|
||||
.getResource("file:/file.txt");
|
||||
assertThat(resource.isFile()).isTrue();
|
||||
assertThat(resource.getFile()).hasParent("/").hasName("file.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWithClassPathIncludesProtocolResolvers() throws IOException {
|
||||
ClassLoader classLoader = new TestClassLoader(this::useTestProtocolResolversFactories);
|
||||
|
||||
Reference in New Issue
Block a user