Skip to content

Commit 48b5b6a

Browse files
committed
Polish "Added support for devtools YAML configuration"
See gh-17915
1 parent 00a3ad0 commit 48b5b6a

File tree

4 files changed

+89
-102
lines changed

4 files changed

+89
-102
lines changed

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

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +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;
22-
import java.util.logging.Logger;
24+
import java.util.function.Function;
2325

2426
import org.springframework.boot.SpringApplication;
2527
import org.springframework.boot.devtools.DevToolsEnablementDeducer;
2628
import org.springframework.boot.env.EnvironmentPostProcessor;
2729
import org.springframework.core.env.ConfigurableEnvironment;
2830
import org.springframework.core.env.PropertiesPropertySource;
31+
import org.springframework.core.env.PropertySource;
2932
import org.springframework.core.io.FileSystemResource;
3033
import org.springframework.core.io.support.PropertiesLoaderUtils;
3134
import org.springframework.util.StringUtils;
@@ -37,46 +40,52 @@
3740
* @author Phillip Webb
3841
* @author Andy Wilkinson
3942
* @author HaiTao Zhang
43+
* @author Madhura Bhave
4044
* @since 1.3.0
4145
*/
4246
public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProcessor {
4347

48+
private static final String LEGACY_FILE_NAME = ".spring-boot-devtools.properties";
49+
4450
private static final String[] FILE_NAMES = new String[] { ".spring-boot-devtools.yml", ".spring-boot-devtools.yaml",
4551
".spring-boot-devtools.properties" };
4652

47-
private Logger logger = Logger.getLogger(getClass().getName());
53+
private static final String CONFIG_PATH = "/.config/spring-boot/";
4854

4955
@Override
5056
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
5157
if (DevToolsEnablementDeducer.shouldEnable(Thread.currentThread())) {
52-
File home = getHomeFolder();
53-
Properties properties = processDir(home, "/.config/spring-boot/", environment);
54-
if (properties.isEmpty()) {
55-
processDir(home, "", environment);
58+
List<PropertySource> propertySources = getPropertySources();
59+
if (propertySources.isEmpty()) {
60+
addPropertySource(LEGACY_FILE_NAME, (file) -> "devtools-local", propertySources);
5661
}
62+
propertySources.forEach((source) -> environment.getPropertySources().addFirst(source));
5763
}
5864
}
5965

60-
private Properties processDir(File home, String configPath, ConfigurableEnvironment environment) {
61-
Properties properties = new Properties();
66+
private List<PropertySource> getPropertySources() {
67+
List<PropertySource> propertySources = new ArrayList<>();
6268
for (String fileName : FILE_NAMES) {
63-
File propertyFile = (home != null) ? new File(home, configPath + fileName) : null;
64-
if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
65-
addProperty(propertyFile, environment, fileName, properties);
66-
}
69+
addPropertySource(CONFIG_PATH + fileName, (file) -> "devtools-local: [" + file.toURI() + "]",
70+
propertySources);
6771
}
68-
return properties;
72+
return propertySources;
6973
}
7074

71-
private void addProperty(File propertyFile, ConfigurableEnvironment environment, String fileName,
72-
Properties properties) {
73-
FileSystemResource resource = new FileSystemResource(propertyFile);
74-
try {
75-
PropertiesLoaderUtils.fillProperties(properties, resource);
76-
environment.getPropertySources().addFirst(new PropertiesPropertySource("devtools-local", properties));
77-
}
78-
catch (IOException ex) {
79-
throw new IllegalStateException("Unable to load " + fileName, ex);
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);
88+
}
8089
}
8190
}
8291

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

Lines changed: 42 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -37,105 +37,68 @@
3737
* @author Phillip Webb
3838
* @author Andy Wilkinson
3939
* @author HaiTao Zhang
40+
* @author Madhura Bhave
4041
*/
4142
class DevToolsHomePropertiesPostProcessorTests {
4243

44+
private String configDir;
45+
4346
private File home;
4447

4548
@BeforeEach
46-
void setup(@TempDir File tempDir) throws IOException {
49+
void setup(@TempDir File tempDir) {
4750
this.home = tempDir;
51+
this.configDir = this.home + "/.config/spring-boot/";
52+
new File(this.configDir).mkdirs();
4853
}
4954

5055
@Test
5156
void loadsPropertiesFromHomeFolderUsingProperties() throws Exception {
5257
Properties properties = new Properties();
5358
properties.put("abc", "def");
54-
OutputStream out = new FileOutputStream(new File(this.home, ".spring-boot-devtools.properties"));
55-
properties.store(out, null);
56-
out.close();
57-
ConfigurableEnvironment environment = new MockEnvironment();
58-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
59-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
60-
assertThat(environment.getProperty("abc")).isEqualTo("def");
61-
}
62-
63-
@Test
64-
void loadsPropertiesFromHomeFolderUsingYml() throws Exception {
65-
Properties properties = new Properties();
66-
properties.put("abc", "def");
67-
OutputStream out = new FileOutputStream(new File(this.home, ".spring-boot-devtools.yml"));
68-
properties.store(out, null);
69-
out.close();
70-
ConfigurableEnvironment environment = new MockEnvironment();
71-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
72-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
73-
assertThat(environment.getProperty("abc")).isEqualTo("def");
74-
}
75-
76-
@Test
77-
void loadsPropertiesFromHomeFolderUsingYaml() throws Exception {
78-
Properties properties = new Properties();
79-
properties.put("abc", "def");
80-
OutputStream out = new FileOutputStream(new File(this.home, ".spring-boot-devtools.yaml"));
81-
properties.store(out, null);
82-
out.close();
83-
ConfigurableEnvironment environment = new MockEnvironment();
84-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
85-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
59+
writeFile(properties, ".spring-boot-devtools.properties");
60+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
8661
assertThat(environment.getProperty("abc")).isEqualTo("def");
8762
}
8863

8964
@Test
9065
void loadsPropertiesFromConfigFolderUsingProperties() throws Exception {
9166
Properties properties = new Properties();
92-
new File(this.home + "/.config/spring-boot").mkdirs();
9367
properties.put("abc", "def");
94-
OutputStream out = new FileOutputStream(
95-
new File(this.home + "/.config/spring-boot", ".spring-boot-devtools.properties"));
68+
OutputStream out = new FileOutputStream(new File(this.configDir, ".spring-boot-devtools.properties"));
9669
properties.store(out, null);
9770
out.close();
98-
ConfigurableEnvironment environment = new MockEnvironment();
99-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
100-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
71+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
10172
assertThat(environment.getProperty("abc")).isEqualTo("def");
10273
}
10374

10475
@Test
10576
void loadsPropertiesFromConfigFolderUsingYml() throws Exception {
10677
Properties properties = new Properties();
107-
new File(this.home + "/.config/spring-boot").mkdirs();
10878
properties.put("abc", "def");
109-
OutputStream out = new FileOutputStream(
110-
new File(this.home + "/.config/spring-boot", ".spring-boot-devtools.yml"));
79+
OutputStream out = new FileOutputStream(new File(this.configDir, ".spring-boot-devtools.yml"));
11180
properties.store(out, null);
11281
out.close();
113-
ConfigurableEnvironment environment = new MockEnvironment();
114-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
115-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
82+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
11683
assertThat(environment.getProperty("abc")).isEqualTo("def");
11784
}
11885

11986
@Test
12087
void loadsPropertiesFromConfigFolderUsingYaml() throws Exception {
12188
Properties properties = new Properties();
122-
new File(this.home + "/.config/spring-boot").mkdirs();
12389
properties.put("abc", "def");
124-
OutputStream out = new FileOutputStream(
125-
new File(this.home + "/.config/spring-boot", ".spring-boot-devtools.yaml"));
90+
OutputStream out = new FileOutputStream(new File(this.configDir, ".spring-boot-devtools.yaml"));
12691
properties.store(out, null);
12792
out.close();
128-
ConfigurableEnvironment environment = new MockEnvironment();
129-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
130-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
93+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
13194
assertThat(environment.getProperty("abc")).isEqualTo("def");
13295
}
13396

13497
@Test
13598
void loadFromConfigFolderWithPropertiesTakingPrecedence() throws Exception {
13699
Properties properties = new Properties();
137100
properties.put("abc", "def");
138-
new File(this.home + "/.config/spring-boot").mkdirs();
101+
properties.put("bar", "baz");
139102
OutputStream out = new FileOutputStream(
140103
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.yaml"));
141104
properties.store(out, null);
@@ -146,57 +109,62 @@ void loadFromConfigFolderWithPropertiesTakingPrecedence() throws Exception {
146109
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.properties"));
147110
properties2.store(out2, null);
148111
out2.close();
149-
ConfigurableEnvironment environment = new MockEnvironment();
150-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
151-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
112+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
152113
assertThat(environment.getProperty("abc")).isEqualTo("jkl");
114+
assertThat(environment.getProperty("bar")).isEqualTo("baz");
153115
}
154116

155117
@Test
156-
void loadFromHomeFolderWithPropertiesTakingPrecedence() throws Exception {
118+
void loadFromConfigFolderTakesPrecedenceOverHomeFolder() throws Exception {
157119
Properties properties = new Properties();
158120
properties.put("abc", "def");
159-
new File(this.home + "/.config/spring-boot").mkdirs();
160-
OutputStream out = new FileOutputStream(new File(this.home, ".spring-boot-devtools.yaml"));
161-
properties.store(out, null);
162-
out.close();
121+
properties.put("bar", "baz");
122+
writeFile(properties, ".spring-boot-devtools.properties");
163123
Properties properties2 = new Properties();
164124
properties2.put("abc", "jkl");
165-
OutputStream out2 = new FileOutputStream(new File(this.home, ".spring-boot-devtools.properties"));
125+
OutputStream out2 = new FileOutputStream(
126+
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.properties"));
166127
properties2.store(out2, null);
167128
out2.close();
168-
ConfigurableEnvironment environment = new MockEnvironment();
169-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
170-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
129+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
171130
assertThat(environment.getProperty("abc")).isEqualTo("jkl");
131+
assertThat(environment.getProperty("bar")).isEqualTo(null);
172132
}
173133

174134
@Test
175-
void loadFromConfigFolderTakesPrecedenceOverHomeFolder() throws Exception {
135+
void loadFromConfigFolderWithYamlTakesPrecedenceOverHomeFolder() throws Exception {
176136
Properties properties = new Properties();
177137
properties.put("abc", "def");
178-
new File(this.home + "/.config/spring-boot").mkdirs();
179-
OutputStream out = new FileOutputStream(new File(this.home, ".spring-boot-devtools.properties"));
180-
properties.store(out, null);
181-
out.close();
138+
properties.put("bar", "baz");
139+
writeFile(properties, ".spring-boot-devtools.properties");
182140
Properties properties2 = new Properties();
183141
properties2.put("abc", "jkl");
184142
OutputStream out2 = new FileOutputStream(
185-
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.properties"));
143+
new File(this.home + "/.config/spring-boot/", ".spring-boot-devtools.yml"));
186144
properties2.store(out2, null);
187145
out2.close();
188-
ConfigurableEnvironment environment = new MockEnvironment();
189-
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
190-
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
146+
ConfigurableEnvironment environment = getPostProcessedEnvironment();
191147
assertThat(environment.getProperty("abc")).isEqualTo("jkl");
148+
assertThat(environment.getProperty("bar")).isEqualTo(null);
192149
}
193150

194151
@Test
195152
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 {
196164
ConfigurableEnvironment environment = new MockEnvironment();
197165
MockDevToolHomePropertiesPostProcessor postProcessor = new MockDevToolHomePropertiesPostProcessor();
198166
runPostProcessor(() -> postProcessor.postProcessEnvironment(environment, null));
199-
assertThat(environment.getProperty("abc")).isNull();
167+
return environment;
200168
}
201169

202170
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)