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
This commit is contained in:
parent
d5de29b76c
commit
fa9a506e3e
|
|
@ -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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
* 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@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"),
|
assertVersionProperty(application.run(configName, "--spring.profiles.active=C"),
|
||||||
"C", "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(
|
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 reverseOrderOfProfilesWithYaml() throws Exception {
|
||||||
|
// gh-322, gh-342
|
||||||
|
SpringApplication application = new SpringApplication(Config.class);
|
||||||
|
application.setWebEnvironment(false);
|
||||||
|
String configName = "--spring.config.name=activeprofilerepro";
|
||||||
assertVersionProperty(
|
assertVersionProperty(
|
||||||
application.run(configName, "--spring.profiles.active=C,A"), "C", "C",
|
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"),
|
||||||
|
"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(
|
||||||
|
application.run(configName, "--spring.profiles.active=A,C"), "C", "A",
|
||||||
|
"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(
|
||||||
|
application.run(configName, "--spring.profiles.active=C,A"), "A", "C",
|
||||||
"A");
|
"A");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
---
|
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
|
||||||
---
|
---
|
||||||
spring.profiles: C
|
spring.profiles: C
|
||||||
version: C
|
version: C
|
||||||
---
|
---
|
||||||
Loading…
Reference in New Issue