Skip to content

Commit 0fee0ca

Browse files
committed
Merge pull request #17915 from htztomic
* pr/17915: Polish "Added support for devtools YAML configuration" Added support for devtools YAML configuration Closes gh-17915
2 parents b54ff7c + 48b5b6a commit 0fee0ca

File tree

4 files changed

+165
-27
lines changed

4 files changed

+165
-27
lines changed

spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessor.java

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818

1919
import java.io.File;
2020
import java.io.IOException;
21+
import java.util.ArrayList;
22+
import java.util.List;
2123
import java.util.Properties;
24+
import java.util.function.Function;
2225

2326
import org.springframework.boot.SpringApplication;
2427
import org.springframework.boot.devtools.DevToolsEnablementDeducer;
2528
import org.springframework.boot.env.EnvironmentPostProcessor;
2629
import org.springframework.core.env.ConfigurableEnvironment;
2730
import org.springframework.core.env.PropertiesPropertySource;
31+
import org.springframework.core.env.PropertySource;
2832
import org.springframework.core.io.FileSystemResource;
2933
import org.springframework.core.io.support.PropertiesLoaderUtils;
3034
import org.springframework.util.StringUtils;
@@ -35,28 +39,52 @@
3539
*
3640
* @author Phillip Webb
3741
* @author Andy Wilkinson
42+
* @author HaiTao Zhang
43+
* @author Madhura Bhave
3844
* @since 1.3.0
3945
*/
4046
public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProcessor {
4147

42-
private static final String FILE_NAME = ".spring-boot-devtools.properties";
48+
private static final String LEGACY_FILE_NAME = ".spring-boot-devtools.properties";
49+
50+
private static final String[] FILE_NAMES = new String[] { ".spring-boot-devtools.yml", ".spring-boot-devtools.yaml",
51+
".spring-boot-devtools.properties" };
52+
53+
private static final String CONFIG_PATH = "/.config/spring-boot/";
4354

4455
@Override
4556
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
4657
if (DevToolsEnablementDeducer.shouldEnable(Thread.currentThread())) {
47-
File home = getHomeFolder();
48-
File propertyFile = (home != null) ? new File(home, FILE_NAME) : null;
49-
if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
50-
FileSystemResource resource = new FileSystemResource(propertyFile);
51-
Properties properties;
52-
try {
53-
properties = PropertiesLoaderUtils.loadProperties(resource);
54-
environment.getPropertySources()
55-
.addFirst(new PropertiesPropertySource("devtools-local", properties));
56-
}
57-
catch (IOException ex) {
58-
throw new IllegalStateException("Unable to load " + FILE_NAME, ex);
59-
}
58+
List<PropertySource> propertySources = getPropertySources();
59+
if (propertySources.isEmpty()) {
60+
addPropertySource(LEGACY_FILE_NAME, (file) -> "devtools-local", propertySources);
61+
}
62+
propertySources.forEach((source) -> environment.getPropertySources().addFirst(source));
63+
}
64+
}
65+
66+
private List<PropertySource> getPropertySources() {
67+
List<PropertySource> propertySources = new ArrayList<>();
68+
for (String fileName : FILE_NAMES) {
69+
addPropertySource(CONFIG_PATH + fileName, (file) -> "devtools-local: [" + file.toURI() + "]",
70+
propertySources);
71+
}
72+
return propertySources;
73+
}
74+
75+
private void addPropertySource(String fileName, Function<File, String> propertySourceName,
76+
List<PropertySource> propertySources) {
77+
Properties properties;
78+
File home = getHomeFolder();
79+
File propertyFile = (home != null) ? new File(home, fileName) : null;
80+
if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
81+
FileSystemResource resource = new FileSystemResource(propertyFile);
82+
try {
83+
properties = PropertiesLoaderUtils.loadProperties(resource);
84+
propertySources.add(new PropertiesPropertySource(propertySourceName.apply(propertyFile), properties));
85+
}
86+
catch (IOException ex) {
87+
throw new IllegalStateException("Unable to load " + fileName, ex);
6088
}
6189
}
6290
}

spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolsHomePropertiesPostProcessorTests.java

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,35 +36,135 @@
3636
*
3737
* @author Phillip Webb
3838
* @author Andy Wilkinson
39+
* @author HaiTao Zhang
40+
* @author Madhura Bhave
3941
*/
4042
class DevToolsHomePropertiesPostProcessorTests {
4143

44+
private String configDir;
45+
4246
private File home;
4347

4448
@BeforeEach
45-
void setup(@TempDir File tempDir) throws IOException {
49+
void setup(@TempDir File tempDir) {
4650
this.home = tempDir;
51+
this.configDir = this.home + "/.config/spring-boot/";
52+
new File(this.configDir).mkdirs();
53+
}
54+
55+
@Test
56+
void loadsPropertiesFromHomeFolderUsingProperties() throws Exception {
57+
Properties properties = new Properties();
58+
properties.put("abc", "def");
59+
writeFile(properties, ".spring-boot-devtools.properties");
60+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
61+
assertThat(environment.getProperty("abc")).isEqualTo("def");
4762
}
4863

4964
@Test
50-
void loadsHomeProperties() throws Exception {
65+
void loadsPropertiesFromConfigFolderUsingProperties() throws Exception {
5166
Properties properties = new Properties();
5267
properties.put("abc", "def");
53-
OutputStream out = new FileOutputStream(new File(this.home, ".spring-boot-devtools.properties"));
68+
OutputStream out = new FileOutputStream(new File(this.configDir, ".spring-boot-devtools.properties"));
5469
properties.store(out, null);
5570
out.close();
56-
ConfigurableEnvironment environment = new MockEnvironment();
57-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
58-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
71+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
72+
assertThat(environment.getProperty("abc")).isEqualTo("def");
73+
}
74+
75+
@Test
76+
void loadsPropertiesFromConfigFolderUsingYml() throws Exception {
77+
Properties properties = new Properties();
78+
properties.put("abc", "def");
79+
OutputStream out = new FileOutputStream(new File(this.configDir, ".spring-boot-devtools.yml"));
80+
properties.store(out, null);
81+
out.close();
82+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
83+
assertThat(environment.getProperty("abc")).isEqualTo("def");
84+
}
85+
86+
@Test
87+
void loadsPropertiesFromConfigFolderUsingYaml() throws Exception {
88+
Properties properties = new Properties();
89+
properties.put("abc", "def");
90+
OutputStream out = new FileOutputStream(new File(this.configDir, ".spring-boot-devtools.yaml"));
91+
properties.store(out, null);
92+
out.close();
93+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
5994
assertThat(environment.getProperty("abc")).isEqualTo("def");
6095
}
6196

97+
@Test
98+
void loadFromConfigFolderWithPropertiesTakingPrecedence() throws Exception {
99+
Properties properties = new Properties();
100+
properties.put("abc", "def");
101+
properties.put("bar", "baz");
102+
OutputStream out = new FileOutputStream(
103+
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.yaml"));
104+
properties.store(out, null);
105+
out.close();
106+
Properties properties2 = new Properties();
107+
properties2.put("abc", "jkl");
108+
OutputStream out2 = new FileOutputStream(
109+
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.properties"));
110+
properties2.store(out2, null);
111+
out2.close();
112+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
113+
assertThat(environment.getProperty("abc")).isEqualTo("jkl");
114+
assertThat(environment.getProperty("bar")).isEqualTo("baz");
115+
}
116+
117+
@Test
118+
void loadFromConfigFolderTakesPrecedenceOverHomeFolder() throws Exception {
119+
Properties properties = new Properties();
120+
properties.put("abc", "def");
121+
properties.put("bar", "baz");
122+
writeFile(properties, ".spring-boot-devtools.properties");
123+
Properties properties2 = new Properties();
124+
properties2.put("abc", "jkl");
125+
OutputStream out2 = new FileOutputStream(
126+
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.properties"));
127+
properties2.store(out2, null);
128+
out2.close();
129+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
130+
assertThat(environment.getProperty("abc")).isEqualTo("jkl");
131+
assertThat(environment.getProperty("bar")).isEqualTo(null);
132+
}
133+
134+
@Test
135+
void loadFromConfigFolderWithYamlTakesPrecedenceOverHomeFolder() throws Exception {
136+
Properties properties = new Properties();
137+
properties.put("abc", "def");
138+
properties.put("bar", "baz");
139+
writeFile(properties, ".spring-boot-devtools.properties");
140+
Properties properties2 = new Properties();
141+
properties2.put("abc", "jkl");
142+
OutputStream out2 = new FileOutputStream(
143+
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.yml"));
144+
properties2.store(out2, null);
145+
out2.close();
146+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
147+
assertThat(environment.getProperty("abc")).isEqualTo("jkl");
148+
assertThat(environment.getProperty("bar")).isEqualTo(null);
149+
}
150+
62151
@Test
63152
void ignoresMissingHomeProperties() throws Exception {
153+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
154+
assertThat(environment.getProperty("abc")).isNull();
155+
}
156+
157+
private void writeFile(Properties properties, String s) throws IOException {
158+
OutputStream out = new FileOutputStream(new File(this.home, s));
159+
properties.store(out, null);
160+
out.close();
161+
}
162+
163+
private ConfigurableEnvironment getPostProcessedEnvironment() throws Exception {
64164
ConfigurableEnvironment environment = new MockEnvironment();
65165
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
66166
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
67-
assertThat(environment.getProperty("abc")).isNull();
167+
return environment;
68168
}
69169

70170
protected void runPostProcessor(Runnable runnable) throws Exception {

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ Spring Boot uses a very particular `PropertySource` order that is designed to al
431431
sensible overriding of values. Properties are considered in the following order:
432432

433433
. <<using-boot-devtools-globalsettings,Devtools global settings properties>>
434-
on your home directory (`~/.spring-boot-devtools.properties` when devtools is active).
434+
in the `$HOME/.config/spring-boot` folder when devtools is active.
435435
. {spring-javadoc}/test/context/TestPropertySource.{dc-ext}[`@TestPropertySource`]
436436
annotations on your tests.
437437
. `properties` attribute on your tests. Available on

spring-boot-project/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,20 +1018,30 @@ from your IDE, only the first has LiveReload support.
10181018

10191019
[[using-boot-devtools-globalsettings]]
10201020
=== Global Settings
1021-
You can configure global devtools settings by adding a file named
1022-
`.spring-boot-devtools.properties` to your `$HOME` folder (note that the filename starts
1023-
with "`.`"). Any properties added to this file apply to _all_ Spring Boot applications on
1021+
You can configure global devtools settings by adding any of the following files to the `$HOME/.config/spring-boot`
1022+
folder (note that the filenames startswith "`.`"):
1023+
1024+
. `.spring-boot-devtools.properties`
1025+
. `.spring-boot-devtools.yaml`
1026+
. `.spring-boot-devtools.yml`
1027+
1028+
Any properties added to these file apply to _all_ Spring Boot applications on
10241029
your machine that use devtools. For example, to configure restart to always use a
10251030
<<using-boot-devtools-restart-triggerfile, trigger file>>, you would add the following
10261031
property:
10271032

1028-
.~/.spring-boot-devtools.properties
1033+
.~/config/spring-boot/.spring-boot-devtools.properties
10291034
[source,properties,indent=0]
10301035
----
10311036
spring.devtools.reload.trigger-file=.reloadtrigger
10321037
----
10331038

1034-
NOTE: Profiles activated in `.spring-boot-devtools.properties` will not affect the
1039+
NOTE: If devtools configuration files are not found in `$HOME/.config/spring-boot`, the root of the `$HOME` folder
1040+
is searched for the presence of a `.spring-boot-devtools.properties` file. This allows you to share the devtools global
1041+
configuration with applications that are on an older version of Spring Boot that does not support the `$HOME/.config/spring-boot`
1042+
location.
1043+
1044+
NOTE: Profiles activated in the above files will not affect the
10351045
loading of <<spring-boot-features.adoc#boot-features-external-config-profile-specific-properties,
10361046
profile-specific configuration files>>.
10371047

0 commit comments

Comments
 (0)