Merge programmatically set active profiles
Update `Profiles` so that any profiles set programmatically on the `Environment` are merged with `spring.profiles.active` properties. Fixes gh-26151 Co-authored-by: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
parent
e8950c7808
commit
cfa26735d2
|
|
@ -26,8 +26,9 @@ import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.bind.BindResult;
|
||||||
import org.springframework.boot.context.properties.bind.Bindable;
|
import org.springframework.boot.context.properties.bind.Bindable;
|
||||||
import org.springframework.boot.context.properties.bind.Binder;
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||||
|
|
@ -61,9 +62,7 @@ public class Profiles implements Iterable<String> {
|
||||||
private static final Bindable<MultiValueMap<String, String>> STRING_STRINGS_MAP = Bindable
|
private static final Bindable<MultiValueMap<String, String>> STRING_STRINGS_MAP = Bindable
|
||||||
.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class));
|
.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class));
|
||||||
|
|
||||||
private static final Set<String> UNSET_ACTIVE = Collections.emptySet();
|
private static final Bindable<Set<String>> STRING_SET = Bindable.setOf(String.class);
|
||||||
|
|
||||||
private static final Set<String> UNSET_DEFAULT = Collections.singleton("default");
|
|
||||||
|
|
||||||
private final MultiValueMap<String, String> groups;
|
private final MultiValueMap<String, String> groups;
|
||||||
|
|
||||||
|
|
@ -86,32 +85,42 @@ public class Profiles implements Iterable<String> {
|
||||||
|
|
||||||
private List<String> getActivatedProfiles(Environment environment, Binder binder,
|
private List<String> getActivatedProfiles(Environment environment, Binder binder,
|
||||||
Collection<String> additionalProfiles) {
|
Collection<String> additionalProfiles) {
|
||||||
return asUniqueItemList(get(environment, binder, environment::getActiveProfiles,
|
return asUniqueItemList(getProfiles(environment, binder, Type.ACTIVE), additionalProfiles);
|
||||||
AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, UNSET_ACTIVE), additionalProfiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getDefaultProfiles(Environment environment, Binder binder) {
|
private List<String> getDefaultProfiles(Environment environment, Binder binder) {
|
||||||
return asUniqueItemList(get(environment, binder, environment::getDefaultProfiles,
|
return asUniqueItemList(getProfiles(environment, binder, Type.DEFAULT));
|
||||||
AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME, UNSET_DEFAULT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] get(Environment environment, Binder binder, Supplier<String[]> supplier, String propertyName,
|
private Collection<String> getProfiles(Environment environment, Binder binder, Type type) {
|
||||||
Set<String> unset) {
|
String environmentPropertyValue = environment.getProperty(type.getName());
|
||||||
String propertyValue = environment.getProperty(propertyName);
|
Set<String> environmentPropertyProfiles = (!StringUtils.hasLength(environmentPropertyValue))
|
||||||
if (hasExplicit(supplier, propertyValue, unset)) {
|
? Collections.emptySet()
|
||||||
return supplier.get();
|
: StringUtils.commaDelimitedListToSet(StringUtils.trimAllWhitespace(environmentPropertyValue));
|
||||||
|
Set<String> environmentProfiles = new LinkedHashSet<>(Arrays.asList(type.get(environment)));
|
||||||
|
BindResult<Set<String>> boundProfiles = binder.bind(type.getName(), STRING_SET);
|
||||||
|
if (hasProgrammaticallySetProfiles(type, environmentPropertyValue, environmentPropertyProfiles,
|
||||||
|
environmentProfiles)) {
|
||||||
|
if (!type.isMergeWithEnvironmentProfiles() || !boundProfiles.isBound()) {
|
||||||
|
return environmentProfiles;
|
||||||
|
}
|
||||||
|
return boundProfiles.map((bound) -> merge(environmentProfiles, bound)).get();
|
||||||
}
|
}
|
||||||
return binder.bind(propertyName, String[].class).orElseGet(() -> StringUtils.toStringArray(unset));
|
return boundProfiles.orElse(type.getDefaultValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasExplicit(Supplier<String[]> supplier, String propertyValue, Set<String> unset) {
|
private boolean hasProgrammaticallySetProfiles(Type type, String environmentPropertyValue,
|
||||||
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(supplier.get()));
|
Set<String> environmentPropertyProfiles, Set<String> environmentProfiles) {
|
||||||
if (!StringUtils.hasLength(propertyValue)) {
|
if (!StringUtils.hasLength(environmentPropertyValue)) {
|
||||||
return !unset.equals(profiles);
|
return !type.getDefaultValue().equals(environmentProfiles);
|
||||||
}
|
}
|
||||||
Set<String> propertyProfiles = StringUtils
|
return !environmentPropertyProfiles.equals(environmentProfiles);
|
||||||
.commaDelimitedListToSet(StringUtils.trimAllWhitespace(propertyValue));
|
}
|
||||||
return !propertyProfiles.equals(profiles);
|
|
||||||
|
private Set<String> merge(Set<String> environmentProfiles, Set<String> bound) {
|
||||||
|
Set<String> result = new LinkedHashSet<>(environmentProfiles);
|
||||||
|
result.addAll(bound);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> expandProfiles(List<String> profiles) {
|
private List<String> expandProfiles(List<String> profiles) {
|
||||||
|
|
@ -124,7 +133,7 @@ public class Profiles implements Iterable<String> {
|
||||||
asReversedList(this.groups.get(current)).forEach(stack::push);
|
asReversedList(this.groups.get(current)).forEach(stack::push);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return asUniqueItemList(StringUtils.toStringArray(expandedProfiles));
|
return asUniqueItemList(expandedProfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> asReversedList(List<String> list) {
|
private List<String> asReversedList(List<String> list) {
|
||||||
|
|
@ -136,12 +145,12 @@ public class Profiles implements Iterable<String> {
|
||||||
return reversed;
|
return reversed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> asUniqueItemList(String[] array) {
|
private List<String> asUniqueItemList(Collection<String> strings) {
|
||||||
return asUniqueItemList(array, null);
|
return asUniqueItemList(strings, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> asUniqueItemList(String[] array, Collection<String> additional) {
|
private List<String> asUniqueItemList(Collection<String> strings, Collection<String> additional) {
|
||||||
LinkedHashSet<String> uniqueItems = new LinkedHashSet<>(Arrays.asList(array));
|
LinkedHashSet<String> uniqueItems = new LinkedHashSet<>(strings);
|
||||||
if (!CollectionUtils.isEmpty(additional)) {
|
if (!CollectionUtils.isEmpty(additional)) {
|
||||||
uniqueItems.addAll(additional);
|
uniqueItems.addAll(additional);
|
||||||
}
|
}
|
||||||
|
|
@ -198,4 +207,49 @@ public class Profiles implements Iterable<String> {
|
||||||
return creator.toString();
|
return creator.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A profiles type that can be obtained.
|
||||||
|
*/
|
||||||
|
private enum Type {
|
||||||
|
|
||||||
|
ACTIVE(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, Environment::getActiveProfiles, true,
|
||||||
|
Collections.emptySet()),
|
||||||
|
|
||||||
|
DEFAULT(AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME, Environment::getDefaultProfiles, false,
|
||||||
|
Collections.singleton("default"));
|
||||||
|
|
||||||
|
private final Function<Environment, String[]> getter;
|
||||||
|
|
||||||
|
private final boolean mergeWithEnvironmentProfiles;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final Set<String> defaultValue;
|
||||||
|
|
||||||
|
Type(String name, Function<Environment, String[]> getter, boolean mergeWithEnvironmentProfiles,
|
||||||
|
Set<String> defaultValue) {
|
||||||
|
this.name = name;
|
||||||
|
this.getter = getter;
|
||||||
|
this.mergeWithEnvironmentProfiles = mergeWithEnvironmentProfiles;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] get(Environment environment) {
|
||||||
|
return this.getter.apply(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> getDefaultValue() {
|
||||||
|
return this.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMergeWithEnvironmentProfiles() {
|
||||||
|
return this.mergeWithEnvironmentProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,16 @@
|
||||||
|
|
||||||
package org.springframework.boot.context.config;
|
package org.springframework.boot.context.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.bind.Binder;
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||||
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
|
@ -69,6 +73,18 @@ class ProfilesTests {
|
||||||
Binder binder = new Binder(
|
Binder binder = new Binder(
|
||||||
new MapConfigurationPropertySource(Collections.singletonMap("spring.profiles.active", "d,e,f")));
|
new MapConfigurationPropertySource(Collections.singletonMap("spring.profiles.active", "d,e,f")));
|
||||||
Profiles profiles = new Profiles(environment, binder, null);
|
Profiles profiles = new Profiles(environment, binder, null);
|
||||||
|
assertThat(profiles.getActive()).containsExactly("a", "b", "c", "d", "e", "f");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getActiveWhenEnvironmentProfilesAndBinderPropertyShouldReturnEnvironmentProperty() {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
environment.setProperty("spring.profiles.active", "a,b,c");
|
||||||
|
List<ConfigurationPropertySource> sources = new ArrayList<>();
|
||||||
|
ConfigurationPropertySources.get(environment).forEach(sources::add);
|
||||||
|
sources.add(new MapConfigurationPropertySource(Collections.singletonMap("spring.profiles.active", "d,e,f")));
|
||||||
|
Binder binder = new Binder(sources);
|
||||||
|
Profiles profiles = new Profiles(environment, binder, null);
|
||||||
assertThat(profiles.getActive()).containsExactly("a", "b", "c");
|
assertThat(profiles.getActive()).containsExactly("a", "b", "c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +95,7 @@ class ProfilesTests {
|
||||||
environment.setProperty("spring.profiles.active", "d,e,f");
|
environment.setProperty("spring.profiles.active", "d,e,f");
|
||||||
Binder binder = Binder.get(environment);
|
Binder binder = Binder.get(environment);
|
||||||
Profiles profiles = new Profiles(environment, binder, null);
|
Profiles profiles = new Profiles(environment, binder, null);
|
||||||
assertThat(profiles.getActive()).containsExactly("a", "b", "c");
|
assertThat(profiles.getActive()).containsExactly("a", "b", "c", "d", "e", "f");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -102,7 +118,7 @@ class ProfilesTests {
|
||||||
environment.setProperty("spring.profiles.active[2]", "f");
|
environment.setProperty("spring.profiles.active[2]", "f");
|
||||||
Binder binder = Binder.get(environment);
|
Binder binder = Binder.get(environment);
|
||||||
Profiles profiles = new Profiles(environment, binder, null);
|
Profiles profiles = new Profiles(environment, binder, null);
|
||||||
assertThat(profiles.getActive()).containsExactly("a", "b", "c");
|
assertThat(profiles.getActive()).containsExactly("a", "b", "c", "d", "e", "f");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -150,6 +166,18 @@ class ProfilesTests {
|
||||||
assertThat(profiles.getDefault()).containsExactly("a", "b", "c");
|
assertThat(profiles.getDefault()).containsExactly("a", "b", "c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getDefaultWhenDefaultEnvironmentProfileAndBinderProperty() {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
environment.setProperty("spring.profiles.default", "default");
|
||||||
|
List<ConfigurationPropertySource> sources = new ArrayList<>();
|
||||||
|
ConfigurationPropertySources.get(environment).forEach(sources::add);
|
||||||
|
sources.add(new MapConfigurationPropertySource(Collections.singletonMap("spring.profiles.default", "a,b,c")));
|
||||||
|
Binder binder = new Binder(sources);
|
||||||
|
Profiles profiles = new Profiles(environment, binder, null);
|
||||||
|
assertThat(profiles.getDefault()).containsExactly("default");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getDefaultWhenNoEnvironmentProfilesAndEnvironmentProperty() {
|
void getDefaultWhenNoEnvironmentProfilesAndEnvironmentProperty() {
|
||||||
MockEnvironment environment = new MockEnvironment();
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
|
@ -210,7 +238,7 @@ class ProfilesTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getDefaultWhenEnvironmentProfilesInBindNotationAndEnvironmentPropertyReturnsEnvironmentProfiles() {
|
void getDefaultWhenEnvironmentProfilesInBindNotationAndEnvironmentPropertyReturnsBoth() {
|
||||||
MockEnvironment environment = new MockEnvironment();
|
MockEnvironment environment = new MockEnvironment();
|
||||||
environment.setDefaultProfiles("a", "b", "c");
|
environment.setDefaultProfiles("a", "b", "c");
|
||||||
environment.setProperty("spring.profiles.default[0]", "d");
|
environment.setProperty("spring.profiles.default[0]", "d");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue