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.List;
|
||||
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.Binder;
|
||||
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
|
||||
.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class));
|
||||
|
||||
private static final Set<String> UNSET_ACTIVE = Collections.emptySet();
|
||||
|
||||
private static final Set<String> UNSET_DEFAULT = Collections.singleton("default");
|
||||
private static final Bindable<Set<String>> STRING_SET = Bindable.setOf(String.class);
|
||||
|
||||
private final MultiValueMap<String, String> groups;
|
||||
|
||||
|
|
@ -86,32 +85,42 @@ public class Profiles implements Iterable<String> {
|
|||
|
||||
private List<String> getActivatedProfiles(Environment environment, Binder binder,
|
||||
Collection<String> additionalProfiles) {
|
||||
return asUniqueItemList(get(environment, binder, environment::getActiveProfiles,
|
||||
AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, UNSET_ACTIVE), additionalProfiles);
|
||||
return asUniqueItemList(getProfiles(environment, binder, Type.ACTIVE), additionalProfiles);
|
||||
}
|
||||
|
||||
private List<String> getDefaultProfiles(Environment environment, Binder binder) {
|
||||
return asUniqueItemList(get(environment, binder, environment::getDefaultProfiles,
|
||||
AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME, UNSET_DEFAULT));
|
||||
return asUniqueItemList(getProfiles(environment, binder, Type.DEFAULT));
|
||||
}
|
||||
|
||||
private String[] get(Environment environment, Binder binder, Supplier<String[]> supplier, String propertyName,
|
||||
Set<String> unset) {
|
||||
String propertyValue = environment.getProperty(propertyName);
|
||||
if (hasExplicit(supplier, propertyValue, unset)) {
|
||||
return supplier.get();
|
||||
private Collection<String> getProfiles(Environment environment, Binder binder, Type type) {
|
||||
String environmentPropertyValue = environment.getProperty(type.getName());
|
||||
Set<String> environmentPropertyProfiles = (!StringUtils.hasLength(environmentPropertyValue))
|
||||
? Collections.emptySet()
|
||||
: 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) {
|
||||
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(supplier.get()));
|
||||
if (!StringUtils.hasLength(propertyValue)) {
|
||||
return !unset.equals(profiles);
|
||||
private boolean hasProgrammaticallySetProfiles(Type type, String environmentPropertyValue,
|
||||
Set<String> environmentPropertyProfiles, Set<String> environmentProfiles) {
|
||||
if (!StringUtils.hasLength(environmentPropertyValue)) {
|
||||
return !type.getDefaultValue().equals(environmentProfiles);
|
||||
}
|
||||
Set<String> propertyProfiles = StringUtils
|
||||
.commaDelimitedListToSet(StringUtils.trimAllWhitespace(propertyValue));
|
||||
return !propertyProfiles.equals(profiles);
|
||||
return !environmentPropertyProfiles.equals(environmentProfiles);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
@ -124,7 +133,7 @@ public class Profiles implements Iterable<String> {
|
|||
asReversedList(this.groups.get(current)).forEach(stack::push);
|
||||
}
|
||||
}
|
||||
return asUniqueItemList(StringUtils.toStringArray(expandedProfiles));
|
||||
return asUniqueItemList(expandedProfiles);
|
||||
}
|
||||
|
||||
private List<String> asReversedList(List<String> list) {
|
||||
|
|
@ -136,12 +145,12 @@ public class Profiles implements Iterable<String> {
|
|||
return reversed;
|
||||
}
|
||||
|
||||
private List<String> asUniqueItemList(String[] array) {
|
||||
return asUniqueItemList(array, null);
|
||||
private List<String> asUniqueItemList(Collection<String> strings) {
|
||||
return asUniqueItemList(strings, null);
|
||||
}
|
||||
|
||||
private List<String> asUniqueItemList(String[] array, Collection<String> additional) {
|
||||
LinkedHashSet<String> uniqueItems = new LinkedHashSet<>(Arrays.asList(array));
|
||||
private List<String> asUniqueItemList(Collection<String> strings, Collection<String> additional) {
|
||||
LinkedHashSet<String> uniqueItems = new LinkedHashSet<>(strings);
|
||||
if (!CollectionUtils.isEmpty(additional)) {
|
||||
uniqueItems.addAll(additional);
|
||||
}
|
||||
|
|
@ -198,4 +207,49 @@ public class Profiles implements Iterable<String> {
|
|||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
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.core.env.Environment;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
|
@ -69,6 +73,18 @@ class ProfilesTests {
|
|||
Binder binder = new Binder(
|
||||
new MapConfigurationPropertySource(Collections.singletonMap("spring.profiles.active", "d,e,f")));
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +95,7 @@ class ProfilesTests {
|
|||
environment.setProperty("spring.profiles.active", "d,e,f");
|
||||
Binder binder = Binder.get(environment);
|
||||
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
|
||||
|
|
@ -102,7 +118,7 @@ class ProfilesTests {
|
|||
environment.setProperty("spring.profiles.active[2]", "f");
|
||||
Binder binder = Binder.get(environment);
|
||||
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
|
||||
|
|
@ -150,6 +166,18 @@ class ProfilesTests {
|
|||
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
|
||||
void getDefaultWhenNoEnvironmentProfilesAndEnvironmentProperty() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
|
|
@ -210,7 +238,7 @@ class ProfilesTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void getDefaultWhenEnvironmentProfilesInBindNotationAndEnvironmentPropertyReturnsEnvironmentProfiles() {
|
||||
void getDefaultWhenEnvironmentProfilesInBindNotationAndEnvironmentPropertyReturnsBoth() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setDefaultProfiles("a", "b", "c");
|
||||
environment.setProperty("spring.profiles.default[0]", "d");
|
||||
|
|
|
|||
Loading…
Reference in New Issue