From e1d21e52d51b751fd32fec81b8093e527f7bb8c5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 4 Jun 2018 15:35:59 -0700 Subject: [PATCH] Allow multi-value spring.profiles properties Update `ConfigFileApplicationListener` to correctly load properties that have `spring.profiles` values containing a list. Prior to this commit the loaded would attempt to add the same document twice resulting in a "property source cannot be added relative to itself" error. Closes gh-13362 --- .../config/ConfigFileApplicationListener.java | 33 ++++++++++++------- .../ConfigFileApplicationListenerTests.java | 11 +++++++ .../resources/applicationmultiprofiles.yml | 6 ++++ 3 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/test/resources/applicationmultiprofiles.yml diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java index 92584ec1d37..62a61f77d45 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -656,27 +657,35 @@ public class ConfigFileApplicationListener private void addLoadedPropertySources() { MutablePropertySources destination = this.environment.getPropertySources(); - String lastAdded = null; List loaded = new ArrayList<>(this.loaded.values()); Collections.reverse(loaded); + String lastAdded = null; + Set added = new HashSet<>(); for (MutablePropertySources sources : loaded) { for (PropertySource source : sources) { - if (lastAdded == null) { - if (destination.contains(DEFAULT_PROPERTIES)) { - destination.addBefore(DEFAULT_PROPERTIES, source); - } - else { - destination.addLast(source); - } - } - else { - destination.addAfter(lastAdded, source); + if (added.add(source.getName())) { + addLoadedPropertySource(destination, lastAdded, source); + lastAdded = source.getName(); } - lastAdded = source.getName(); } } } + private void addLoadedPropertySource(MutablePropertySources destination, + String lastAdded, PropertySource source) { + if (lastAdded == null) { + if (destination.contains(DEFAULT_PROPERTIES)) { + destination.addBefore(DEFAULT_PROPERTIES, source); + } + else { + destination.addLast(source); + } + } + else { + destination.addAfter(lastAdded, source); + } + } + } /** diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java index e0be4d14625..1d82d42e113 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java @@ -900,7 +900,18 @@ public class ConfigFileApplicationListenerTests { this.context = application.run("--spring.config.name=applicationloop"); ConfigurableEnvironment environment = this.context.getEnvironment(); assertThat(environment.acceptsProfiles("loop")).isTrue(); + } + @Test + public void multiValueSpringProfiles() { + // gh-13362 + SpringApplication application = new SpringApplication(Config.class); + application.setWebApplicationType(WebApplicationType.NONE); + this.context = application.run("--spring.config.name=applicationmultiprofiles"); + ConfigurableEnvironment environment = this.context.getEnvironment(); + assertThat(environment.acceptsProfiles("test")).isTrue(); + assertThat(environment.acceptsProfiles("another-test")).isTrue(); + assertThat(environment.getProperty("message")).isEqualTo("multiprofile"); } private Condition matchingPropertySource( diff --git a/spring-boot-project/spring-boot/src/test/resources/applicationmultiprofiles.yml b/spring-boot-project/spring-boot/src/test/resources/applicationmultiprofiles.yml new file mode 100644 index 00000000000..bf406504019 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/applicationmultiprofiles.yml @@ -0,0 +1,6 @@ +spring.profiles.active: test, another-test +message: default +--- +spring: + profiles: test,another-test +message: multiprofile