From 8e7a6ceb449657c3ddf5bb2b5f586ffb4e4a84c9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 16 Jul 2021 13:13:17 +0100 Subject: [PATCH] Polish "Make dev tools' home directory configurable" See gh-17924 --- .../DevToolsHomePropertiesPostProcessor.java | 42 ++++++---- ...ToolsHomePropertiesPostProcessorTests.java | 79 ++++++++++++------- .../src/docs/asciidoc/using/devtools.adoc | 3 + 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java index 207827f0f9..a1ba77373c 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java @@ -23,8 +23,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.function.Function; +import java.util.function.Supplier; import org.springframework.boot.SpringApplication; import org.springframework.boot.devtools.system.DevToolsEnablementDeducer; @@ -59,6 +62,10 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce private static final Set PROPERTY_SOURCE_LOADERS; + private final Properties systemProperties; + + private final Map environmentVariables; + static { Set propertySourceLoaders = new HashSet<>(); propertySourceLoaders.add(new PropertiesPropertySourceLoader()); @@ -68,6 +75,15 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce PROPERTY_SOURCE_LOADERS = Collections.unmodifiableSet(propertySourceLoaders); } + public DevToolsHomePropertiesPostProcessor() { + this(System.getenv(), System.getProperties()); + } + + DevToolsHomePropertiesPostProcessor(Map environmentVariables, Properties systemProperties) { + this.environmentVariables = environmentVariables; + this.systemProperties = systemProperties; + } + @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { if (DevToolsEnablementDeducer.shouldEnable(Thread.currentThread())) { @@ -93,7 +109,7 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce private void addPropertySource(List> propertySources, String fileName, Function propertySourceNamer) { - File home = getProjectRootFolder(); + File home = getHomeDirectory(); File file = (home != null) ? new File(home, fileName) : null; FileSystemResource resource = (file != null) ? new FileSystemResource(file) : null; if (resource != null && resource.exists() && resource.isFile()) { @@ -121,21 +137,19 @@ public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProce .anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(name, fileExtension)); } - protected File getProjectRootFolder() { - String rootFolder = System.getenv("PROJECT_ROOT_FOLDER"); - if (rootFolder == null) { - rootFolder = System.getProperty("PROJECT_ROOT_FOLDER"); - } - if (StringUtils.hasLength(rootFolder)) { - return new File(rootFolder); - } - return getHomeDirectory(); + protected File getHomeDirectory() { + return getHomeDirectory(() -> this.environmentVariables.get("SPRING_DEVTOOLS_HOME"), + () -> this.systemProperties.getProperty("spring.devtools.home"), + () -> this.systemProperties.getProperty("user.home")); } - protected File getHomeDirectory() { - String home = System.getProperty("user.home"); - if (StringUtils.hasLength(home)) { - return new File(home); + @SafeVarargs + private final File getHomeDirectory(Supplier... pathSuppliers) { + for (Supplier pathSupplier : pathSuppliers) { + String path = pathSupplier.get(); + if (StringUtils.hasText(path)) { + return new File(path); + } } return null; } diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessorTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessorTests.java index 7d86429a5a..8505cb842d 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessorTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessorTests.java @@ -21,6 +21,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; +import java.util.Collections; +import java.util.Map; import java.util.Properties; import org.junit.jupiter.api.BeforeEach; @@ -47,12 +49,12 @@ class DevToolsHomePropertiesPostProcessorTests { private File home; - private File rootDir; + private File customHome; @BeforeEach - void setup(@TempDir File tempDir, @TempDir File rootDir) { - this.home = tempDir; - this.rootDir = rootDir; + void setup(@TempDir File tempDir) { + this.home = new File(tempDir, "default-home"); + this.customHome = new File(tempDir, "custom-home"); this.configDir = this.home + "/.config/spring-boot/"; new File(this.configDir).mkdirs(); } @@ -67,17 +69,25 @@ class DevToolsHomePropertiesPostProcessorTests { } @Test - void loadsRootFolderProperties() throws Exception { - System.setProperty("PROJECT_ROOT_FOLDER", this.rootDir.getAbsolutePath()); + void loadsPropertiesFromCustomHomeDirectorySetUsingSystemProperty() throws Exception { Properties properties = new Properties(); properties.put("uvw", "xyz"); - OutputStream out = new FileOutputStream( - new File(this.rootDir, ".config/spring-boot/spring-boot-devtools.properties")); - properties.store(out, null); - out.close(); - ConfigurableEnvironment environment = new MockEnvironment(); - MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor(); - runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null)); + writeFile(properties, this.customHome, ".config/spring-boot/spring-boot-devtools.properties"); + Properties systemProperties = new Properties(); + systemProperties.setProperty("spring.devtools.home", this.customHome.getAbsolutePath()); + ConfigurableEnvironment environment = getPostProcessedEnvironment(systemProperties); + assertThat(environment.getProperty("uvw")).isEqualTo("xyz"); + } + + @Test + void loadsPropertiesFromCustomHomeDirectorySetUsingEnvironmentVariable() throws Exception { + Properties properties = new Properties(); + properties.put("uvw", "xyz"); + writeFile(properties, this.customHome, ".config/spring-boot/spring-boot-devtools.properties"); + Properties systemProperties = new Properties(); + systemProperties.setProperty("spring.devtools.home", this.customHome.getAbsolutePath()); + ConfigurableEnvironment environment = getPostProcessedEnvironment( + Collections.singletonMap("SPRING_DEVTOOLS_HOME", this.customHome.getAbsolutePath())); assertThat(environment.getProperty("uvw")).isEqualTo("xyz"); } @@ -169,15 +179,39 @@ class DevToolsHomePropertiesPostProcessorTests { assertThat(environment.getProperty("abc")).isNull(); } - private void writeFile(Properties properties, String s) throws IOException { - OutputStream out = new FileOutputStream(new File(this.home, s)); - properties.store(out, null); - out.close(); + private void writeFile(Properties properties, String path) throws IOException { + writeFile(properties, this.home, path); + } + + private void writeFile(Properties properties, File home, String path) throws IOException { + File file = new File(home, path); + file.getParentFile().mkdirs(); + try (OutputStream out = new FileOutputStream(file)) { + properties.store(out, null); + } } private ConfigurableEnvironment getPostProcessedEnvironment() throws Exception { + return getPostProcessedEnvironment(null, null); + } + + private ConfigurableEnvironment getPostProcessedEnvironment(Properties systemProperties) throws Exception { + return getPostProcessedEnvironment(null, systemProperties); + } + + private ConfigurableEnvironment getPostProcessedEnvironment(Map env) throws Exception { + return getPostProcessedEnvironment(env, null); + } + + private ConfigurableEnvironment getPostProcessedEnvironment(Map env, Properties systemProperties) + throws Exception { + if (systemProperties == null) { + systemProperties = new Properties(); + systemProperties.setProperty("user.home", this.home.getAbsolutePath()); + } ConfigurableEnvironment environment = new MockEnvironment(); - MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor(); + DevToolsHomePropertiesPostProcessor postProcessor = new DevToolsHomePropertiesPostProcessor( + (env != null) ? env : Collections.emptyMap(), systemProperties); runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null)); return environment; } @@ -188,13 +222,4 @@ class DevToolsHomePropertiesPostProcessorTests { thread.join(); } - private class MockDevToolHomePropertiesPostProcessor extends DevToolsHomePropertiesPostProcessor { - - @Override - protected File getHomeDirectory() { - return DevToolsHomePropertiesPostProcessorTests.this.home; - } - - } - } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc index 2e534f0152..fedd92b08f 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc @@ -285,6 +285,9 @@ For example, to configure restart to always use a <