Skip to content

Commit fa9a506

Browse files
author
Dave Syer
committed
Straighten out profile ordering semantics
Here's what I think works best: * Any profile in the Environment before application.yml is processed takes precedence (i.e. it will be last in the list of active profiles in the live app) * Any profile in the Environment before SpringApplication starts takes precedence (so any added on the command line or with System properties come after ones added using the SpringApplication API) * The order of profiles in application.yml is irrelevant - profiles are applied in the order they come out of Environment.getActiveProfiles() Fixes gh-342
1 parent d5de29b commit fa9a506

File tree

7 files changed

+136
-42
lines changed

7 files changed

+136
-42
lines changed

spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,14 @@ protected void addPropertySources(ConfigurableEnvironment environment, String[]
415415
* @param environment the environment to configure
416416
*/
417417
protected void setupProfiles(ConfigurableEnvironment environment) {
418+
Set<String> profiles = new LinkedHashSet<String>();
419+
environment.getActiveProfiles(); // ensure they are initialized
420+
// But these ones should go first (last wins in a property key clash)
418421
for (String profile : this.profiles) {
419-
environment.addActiveProfile(profile);
422+
profiles.add(profile);
420423
}
424+
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
425+
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
421426
}
422427

423428
/**

spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,15 @@ public Loader(ConfigurableEnvironment environment) {
264264

265265
public void load() throws IOException {
266266
this.propertiesLoader = new PropertySourcesLoader();
267-
this.profiles = new LinkedList<String>();
268-
this.profiles.add(null);
269-
this.profiles.addAll(Arrays.asList(this.environment.getActiveProfiles()));
267+
this.profiles = Collections.asLifoQueue(new LinkedList<String>());
270268
this.activatedProfiles = false;
271-
addActiveProfiles(this.environment.getProperty(ACTIVE_PROFILES_PROPERTY));
269+
270+
// Any pre-existing active profiles take precedence over those added in
271+
// config files (unless latter are prefixed with "+").
272+
addActiveProfiles(StringUtils.arrayToCommaDelimitedString(this.environment
273+
.getActiveProfiles()));
274+
275+
this.profiles.add(null);
272276

273277
while (!this.profiles.isEmpty()) {
274278
String profile = this.profiles.poll();
@@ -322,16 +326,29 @@ private void addActiveProfiles(Object property) {
322326
String profiles = (property == null ? null : property.toString());
323327
boolean profilesNotActivatedWhenCalled = !this.activatedProfiles;
324328
for (String profile : asResolvedSet(profiles, null)) {
329+
// A profile name prefixed with "+" is always added even if it is
330+
// activated in a config file (without the "+" it can be disabled
331+
// by an explicit Environment property set before the file was
332+
// processed).
325333
boolean addition = profile.startsWith("+");
326334
profile = (addition ? profile.substring(1) : profile);
327335
if (profilesNotActivatedWhenCalled || addition) {
328336
this.profiles.add(profile);
329-
this.environment.addActiveProfile(profile);
337+
prependProfile(this.environment, profile);
330338
this.activatedProfiles = true;
331339
}
332340
}
333341
}
334342

343+
private void prependProfile(ConfigurableEnvironment environment, String profile) {
344+
Set<String> profiles = new LinkedHashSet<String>();
345+
environment.getActiveProfiles(); // ensure they are initialized
346+
// But this one should go first (last wins in a property key clash)
347+
profiles.add(profile);
348+
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
349+
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
350+
}
351+
335352
public Set<String> getSearchLocations() {
336353
Set<String> locations = new LinkedHashSet<String>();
337354
locations.addAll(asResolvedSet(
@@ -361,10 +378,16 @@ public Set<String> getSearchNames() {
361378
}
362379

363380
private Set<String> asResolvedSet(String value, String fallback) {
381+
return asResolvedSet(value, fallback, true);
382+
}
383+
384+
private Set<String> asResolvedSet(String value, String fallback, boolean reverse) {
364385
List<String> list = Arrays.asList(StringUtils
365386
.commaDelimitedListToStringArray(value != null ? this.environment
366387
.resolvePlaceholders(value) : fallback));
367-
Collections.reverse(list);
388+
if (reverse) {
389+
Collections.reverse(list);
390+
}
368391
return new LinkedHashSet<String>(list);
369392
}
370393

spring-boot/src/test/java/org/springframework/boot/ReproTests.java

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* Tests to reproduce reported issues.
2828
*
2929
* @author Phillip Webb
30+
* @author Dave Syer
3031
*/
3132
public class ReproTests {
3233

@@ -44,23 +45,113 @@ public void enableProfileViaApplicationProperties() throws Exception {
4445
}
4546

4647
@Test
47-
public void activeProfilesWithYaml() throws Exception {
48-
// gh-322
48+
public void activeProfilesWithYamlAndCommandLine() throws Exception {
49+
// gh-322, gh-342
4950
SpringApplication application = new SpringApplication(Config.class);
5051
application.setWebEnvironment(false);
5152
String configName = "--spring.config.name=activeprofilerepro";
5253
assertVersionProperty(application.run(configName, "--spring.profiles.active=B"),
5354
"B", "B");
55+
}
56+
57+
@Test
58+
public void activeProfilesWithYamlOnly() throws Exception {
59+
// gh-322, gh-342
60+
SpringApplication application = new SpringApplication(Config.class);
61+
application.setWebEnvironment(false);
62+
String configName = "--spring.config.name=activeprofilerepro";
5463
assertVersionProperty(application.run(configName), "B", "B");
55-
assertVersionProperty(application.run(configName, "--spring.profiles.active=A"),
56-
"A", "A");
64+
}
65+
66+
@Test
67+
public void orderActiveProfilesWithYamlOnly() throws Exception {
68+
// gh-322, gh-342
69+
SpringApplication application = new SpringApplication(Config.class);
70+
application.setWebEnvironment(false);
71+
String configName = "--spring.config.name=activeprofilerepro-ordered";
72+
assertVersionProperty(application.run(configName), "B", "A", "B");
73+
}
74+
75+
@Test
76+
public void commandLineBeatsProfilesWithYaml() throws Exception {
77+
// gh-322, gh-342
78+
SpringApplication application = new SpringApplication(Config.class);
79+
application.setWebEnvironment(false);
80+
String configName = "--spring.config.name=activeprofilerepro";
81+
assertVersionProperty(application.run(configName, "--spring.profiles.active=C"),
82+
"C", "C");
83+
}
84+
85+
@Test
86+
public void orderProfilesWithYaml() throws Exception {
87+
// gh-322, gh-342
88+
SpringApplication application = new SpringApplication(Config.class);
89+
application.setWebEnvironment(false);
90+
String configName = "--spring.config.name=activeprofilerepro";
91+
assertVersionProperty(
92+
application.run(configName, "--spring.profiles.active=A,C"), "C", "A",
93+
"C");
94+
}
95+
96+
@Test
97+
public void reverseOrderOfProfilesWithYaml() throws Exception {
98+
// gh-322, gh-342
99+
SpringApplication application = new SpringApplication(Config.class);
100+
application.setWebEnvironment(false);
101+
String configName = "--spring.config.name=activeprofilerepro";
102+
assertVersionProperty(
103+
application.run(configName, "--spring.profiles.active=C,A"), "A", "C",
104+
"A");
105+
}
106+
107+
@Test
108+
public void activeProfilesWithYamlAndCommandLineAndNoOverride() throws Exception {
109+
// gh-322, gh-342
110+
SpringApplication application = new SpringApplication(Config.class);
111+
application.setWebEnvironment(false);
112+
String configName = "--spring.config.name=activeprofilerepro-without-override";
113+
assertVersionProperty(application.run(configName, "--spring.profiles.active=B"),
114+
"B", "B");
115+
}
116+
117+
@Test
118+
public void activeProfilesWithYamlOnlyAndNoOverride() throws Exception {
119+
// gh-322, gh-342
120+
SpringApplication application = new SpringApplication(Config.class);
121+
application.setWebEnvironment(false);
122+
String configName = "--spring.config.name=activeprofilerepro-without-override";
123+
assertVersionProperty(application.run(configName), null);
124+
}
125+
126+
@Test
127+
public void commandLineBeatsProfilesWithYamlAndNoOverride() throws Exception {
128+
// gh-322, gh-342
129+
SpringApplication application = new SpringApplication(Config.class);
130+
application.setWebEnvironment(false);
131+
String configName = "--spring.config.name=activeprofilerepro-without-override";
57132
assertVersionProperty(application.run(configName, "--spring.profiles.active=C"),
58133
"C", "C");
134+
}
135+
136+
@Test
137+
public void orderProfilesWithYamlAndNoOverride() throws Exception {
138+
// gh-322, gh-342
139+
SpringApplication application = new SpringApplication(Config.class);
140+
application.setWebEnvironment(false);
141+
String configName = "--spring.config.name=activeprofilerepro-without-override";
59142
assertVersionProperty(
60-
application.run(configName, "--spring.profiles.active=A,C"), "A", "A",
143+
application.run(configName, "--spring.profiles.active=A,C"), "C", "A",
61144
"C");
145+
}
146+
147+
@Test
148+
public void reverseOrderOfProfilesWithYamlAndNoOverride() throws Exception {
149+
// gh-322, gh-342
150+
SpringApplication application = new SpringApplication(Config.class);
151+
application.setWebEnvironment(false);
152+
String configName = "--spring.config.name=activeprofilerepro-without-override";
62153
assertVersionProperty(
63-
application.run(configName, "--spring.profiles.active=C,A"), "C", "C",
154+
application.run(configName, "--spring.profiles.active=C,A"), "A", "C",
64155
"A");
65156
}
66157

spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@ public void addProfilesOrder() throws Exception {
297297
ConfigurableEnvironment environment = new StandardEnvironment();
298298
application.setEnvironment(environment);
299299
application.run("--spring.profiles.active=bar");
300-
// Command line arguably should always come last (not the case currently)
301-
assertArrayEquals(new String[] { "bar", "foo" }, environment.getActiveProfiles());
300+
// Command line should always come last
301+
assertArrayEquals(new String[] { "foo", "bar" }, environment.getActiveProfiles());
302302
}
303303

304304
@Test

spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -207,30 +207,6 @@ public void yamlProfileCanBeChanged() throws Exception {
207207
assertThat(this.environment.getActiveProfiles(), equalTo(new String[] { "prod" }));
208208
}
209209

210-
@Test
211-
public void yamlProfileOrdering() throws Exception {
212-
this.initializer.setSearchNames("threeprofiles");
213-
this.environment.setActiveProfiles("A", "C");
214-
this.initializer.onApplicationEvent(this.event);
215-
assertThat(this.environment.getProperty("version"), equalTo("C"));
216-
}
217-
218-
@Test
219-
public void yamlProfileOrderingReverse() throws Exception {
220-
this.initializer.setSearchNames("threeprofiles");
221-
this.environment.setActiveProfiles("C", "A");
222-
this.initializer.onApplicationEvent(this.event);
223-
assertThat(this.environment.getProperty("version"), equalTo("A"));
224-
}
225-
226-
@Test
227-
public void yamlProfileOrderingOverride() throws Exception {
228-
this.initializer.setSearchNames("threeprofiles-with-override");
229-
this.environment.setActiveProfiles("C", "A");
230-
this.initializer.onApplicationEvent(this.event);
231-
assertThat(this.environment.getProperty("version"), equalTo("B"));
232-
}
233-
234210
@Test
235211
public void specificNameAndProfileFromExistingSource() throws Exception {
236212
EnvironmentTestUtils.addEnvironment(this.environment,
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
---
2-
spring.profiles.active: B
1+
spring.profiles.active: A,B
32
---
43
spring.profiles: A
54
version: A
@@ -9,4 +8,4 @@ version: B
98
---
109
spring.profiles: C
1110
version: C
12-
---
11+
---

0 commit comments

Comments
 (0)