Commit fa9a506e authored by Dave Syer's avatar Dave Syer

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
parent d5de29b7
...@@ -415,9 +415,14 @@ public class SpringApplication { ...@@ -415,9 +415,14 @@ public class SpringApplication {
* @param environment the environment to configure * @param environment the environment to configure
*/ */
protected void setupProfiles(ConfigurableEnvironment environment) { protected void setupProfiles(ConfigurableEnvironment environment) {
Set<String> profiles = new LinkedHashSet<String>();
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
for (String profile : this.profiles) { for (String profile : this.profiles) {
environment.addActiveProfile(profile); profiles.add(profile);
} }
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
} }
/** /**
......
...@@ -264,11 +264,15 @@ public class ConfigFileApplicationListener implements ...@@ -264,11 +264,15 @@ public class ConfigFileApplicationListener implements
public void load() throws IOException { public void load() throws IOException {
this.propertiesLoader = new PropertySourcesLoader(); this.propertiesLoader = new PropertySourcesLoader();
this.profiles = new LinkedList<String>(); this.profiles = Collections.asLifoQueue(new LinkedList<String>());
this.profiles.add(null);
this.profiles.addAll(Arrays.asList(this.environment.getActiveProfiles()));
this.activatedProfiles = false; this.activatedProfiles = false;
addActiveProfiles(this.environment.getProperty(ACTIVE_PROFILES_PROPERTY));
// Any pre-existing active profiles take precedence over those added in
// config files (unless latter are prefixed with "+").
addActiveProfiles(StringUtils.arrayToCommaDelimitedString(this.environment
.getActiveProfiles()));
this.profiles.add(null);
while (!this.profiles.isEmpty()) { while (!this.profiles.isEmpty()) {
String profile = this.profiles.poll(); String profile = this.profiles.poll();
...@@ -322,16 +326,29 @@ public class ConfigFileApplicationListener implements ...@@ -322,16 +326,29 @@ public class ConfigFileApplicationListener implements
String profiles = (property == null ? null : property.toString()); String profiles = (property == null ? null : property.toString());
boolean profilesNotActivatedWhenCalled = !this.activatedProfiles; boolean profilesNotActivatedWhenCalled = !this.activatedProfiles;
for (String profile : asResolvedSet(profiles, null)) { for (String profile : asResolvedSet(profiles, null)) {
// A profile name prefixed with "+" is always added even if it is
// activated in a config file (without the "+" it can be disabled
// by an explicit Environment property set before the file was
// processed).
boolean addition = profile.startsWith("+"); boolean addition = profile.startsWith("+");
profile = (addition ? profile.substring(1) : profile); profile = (addition ? profile.substring(1) : profile);
if (profilesNotActivatedWhenCalled || addition) { if (profilesNotActivatedWhenCalled || addition) {
this.profiles.add(profile); this.profiles.add(profile);
this.environment.addActiveProfile(profile); prependProfile(this.environment, profile);
this.activatedProfiles = true; this.activatedProfiles = true;
} }
} }
} }
private void prependProfile(ConfigurableEnvironment environment, String profile) {
Set<String> profiles = new LinkedHashSet<String>();
environment.getActiveProfiles(); // ensure they are initialized
// But this one should go first (last wins in a property key clash)
profiles.add(profile);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
}
public Set<String> getSearchLocations() { public Set<String> getSearchLocations() {
Set<String> locations = new LinkedHashSet<String>(); Set<String> locations = new LinkedHashSet<String>();
locations.addAll(asResolvedSet( locations.addAll(asResolvedSet(
...@@ -361,10 +378,16 @@ public class ConfigFileApplicationListener implements ...@@ -361,10 +378,16 @@ public class ConfigFileApplicationListener implements
} }
private Set<String> asResolvedSet(String value, String fallback) { private Set<String> asResolvedSet(String value, String fallback) {
return asResolvedSet(value, fallback, true);
}
private Set<String> asResolvedSet(String value, String fallback, boolean reverse) {
List<String> list = Arrays.asList(StringUtils List<String> list = Arrays.asList(StringUtils
.commaDelimitedListToStringArray(value != null ? this.environment .commaDelimitedListToStringArray(value != null ? this.environment
.resolvePlaceholders(value) : fallback)); .resolvePlaceholders(value) : fallback));
Collections.reverse(list); if (reverse) {
Collections.reverse(list);
}
return new LinkedHashSet<String>(list); return new LinkedHashSet<String>(list);
} }
......
...@@ -27,6 +27,7 @@ import static org.junit.Assert.assertThat; ...@@ -27,6 +27,7 @@ import static org.junit.Assert.assertThat;
* Tests to reproduce reported issues. * Tests to reproduce reported issues.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer
*/ */
public class ReproTests { public class ReproTests {
...@@ -44,23 +45,113 @@ public class ReproTests { ...@@ -44,23 +45,113 @@ public class ReproTests {
} }
@Test @Test
public void activeProfilesWithYaml() throws Exception { public void activeProfilesWithYamlAndCommandLine() throws Exception {
// gh-322 // gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class); SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false); application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro"; String configName = "--spring.config.name=activeprofilerepro";
assertVersionProperty(application.run(configName, "--spring.profiles.active=B"), assertVersionProperty(application.run(configName, "--spring.profiles.active=B"),
"B", "B"); "B", "B");
}
@Test
public void activeProfilesWithYamlOnly() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro";
assertVersionProperty(application.run(configName), "B", "B"); assertVersionProperty(application.run(configName), "B", "B");
assertVersionProperty(application.run(configName, "--spring.profiles.active=A"), }
"A", "A");
@Test
public void orderActiveProfilesWithYamlOnly() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro-ordered";
assertVersionProperty(application.run(configName), "B", "A", "B");
}
@Test
public void commandLineBeatsProfilesWithYaml() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro";
assertVersionProperty(application.run(configName, "--spring.profiles.active=C"),
"C", "C");
}
@Test
public void orderProfilesWithYaml() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro";
assertVersionProperty(
application.run(configName, "--spring.profiles.active=A,C"), "C", "A",
"C");
}
@Test
public void reverseOrderOfProfilesWithYaml() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro";
assertVersionProperty(
application.run(configName, "--spring.profiles.active=C,A"), "A", "C",
"A");
}
@Test
public void activeProfilesWithYamlAndCommandLineAndNoOverride() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro-without-override";
assertVersionProperty(application.run(configName, "--spring.profiles.active=B"),
"B", "B");
}
@Test
public void activeProfilesWithYamlOnlyAndNoOverride() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro-without-override";
assertVersionProperty(application.run(configName), null);
}
@Test
public void commandLineBeatsProfilesWithYamlAndNoOverride() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro-without-override";
assertVersionProperty(application.run(configName, "--spring.profiles.active=C"), assertVersionProperty(application.run(configName, "--spring.profiles.active=C"),
"C", "C"); "C", "C");
}
@Test
public void orderProfilesWithYamlAndNoOverride() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro-without-override";
assertVersionProperty( assertVersionProperty(
application.run(configName, "--spring.profiles.active=A,C"), "A", "A", application.run(configName, "--spring.profiles.active=A,C"), "C", "A",
"C"); "C");
}
@Test
public void reverseOrderOfProfilesWithYamlAndNoOverride() throws Exception {
// gh-322, gh-342
SpringApplication application = new SpringApplication(Config.class);
application.setWebEnvironment(false);
String configName = "--spring.config.name=activeprofilerepro-without-override";
assertVersionProperty( assertVersionProperty(
application.run(configName, "--spring.profiles.active=C,A"), "C", "C", application.run(configName, "--spring.profiles.active=C,A"), "A", "C",
"A"); "A");
} }
......
...@@ -297,8 +297,8 @@ public class SpringApplicationTests { ...@@ -297,8 +297,8 @@ public class SpringApplicationTests {
ConfigurableEnvironment environment = new StandardEnvironment(); ConfigurableEnvironment environment = new StandardEnvironment();
application.setEnvironment(environment); application.setEnvironment(environment);
application.run("--spring.profiles.active=bar"); application.run("--spring.profiles.active=bar");
// Command line arguably should always come last (not the case currently) // Command line should always come last
assertArrayEquals(new String[] { "bar", "foo" }, environment.getActiveProfiles()); assertArrayEquals(new String[] { "foo", "bar" }, environment.getActiveProfiles());
} }
@Test @Test
......
...@@ -207,30 +207,6 @@ public class ConfigFileApplicationListenerTests { ...@@ -207,30 +207,6 @@ public class ConfigFileApplicationListenerTests {
assertThat(this.environment.getActiveProfiles(), equalTo(new String[] { "prod" })); assertThat(this.environment.getActiveProfiles(), equalTo(new String[] { "prod" }));
} }
@Test
public void yamlProfileOrdering() throws Exception {
this.initializer.setSearchNames("threeprofiles");
this.environment.setActiveProfiles("A", "C");
this.initializer.onApplicationEvent(this.event);
assertThat(this.environment.getProperty("version"), equalTo("C"));
}
@Test
public void yamlProfileOrderingReverse() throws Exception {
this.initializer.setSearchNames("threeprofiles");
this.environment.setActiveProfiles("C", "A");
this.initializer.onApplicationEvent(this.event);
assertThat(this.environment.getProperty("version"), equalTo("A"));
}
@Test
public void yamlProfileOrderingOverride() throws Exception {
this.initializer.setSearchNames("threeprofiles-with-override");
this.environment.setActiveProfiles("C", "A");
this.initializer.onApplicationEvent(this.event);
assertThat(this.environment.getProperty("version"), equalTo("B"));
}
@Test @Test
public void specificNameAndProfileFromExistingSource() throws Exception { public void specificNameAndProfileFromExistingSource() throws Exception {
EnvironmentTestUtils.addEnvironment(this.environment, EnvironmentTestUtils.addEnvironment(this.environment,
......
--- spring.profiles.active: A,B
spring.profiles.active: B
--- ---
spring.profiles: A spring.profiles: A
version: A version: A
...@@ -9,4 +8,4 @@ version: B ...@@ -9,4 +8,4 @@ version: B
--- ---
spring.profiles: C spring.profiles: C
version: C version: C
--- ---
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment