Add limited support for spring.profiles.include
Restore support for the `spring.profiles.include` property but only for non-profile specific documents. Closes gh-22944
This commit is contained in:
parent
6cfd2e3a33
commit
c1be5cb5e0
|
@ -18,13 +18,20 @@ package org.springframework.boot.context.config;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
||||||
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
|
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
|
||||||
import org.springframework.boot.context.properties.bind.BindException;
|
import org.springframework.boot.context.properties.bind.BindException;
|
||||||
|
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.bind.PlaceholdersResolver;
|
||||||
|
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||||
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||||
import org.springframework.boot.env.BootstrapRegistry;
|
import org.springframework.boot.env.BootstrapRegistry;
|
||||||
import org.springframework.boot.env.DefaultPropertiesPropertySource;
|
import org.springframework.boot.env.DefaultPropertiesPropertySource;
|
||||||
import org.springframework.boot.logging.DeferredLogFactory;
|
import org.springframework.boot.logging.DeferredLogFactory;
|
||||||
|
@ -74,6 +81,11 @@ class ConfigDataEnvironment {
|
||||||
|
|
||||||
private static final String[] EMPTY_LOCATIONS = new String[0];
|
private static final String[] EMPTY_LOCATIONS = new String[0];
|
||||||
|
|
||||||
|
private static final ConfigurationPropertyName INCLUDE_PROFILES = ConfigurationPropertyName
|
||||||
|
.of(Profiles.INCLUDE_PROFILES_PROPERTY_NAME);
|
||||||
|
|
||||||
|
private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
|
||||||
|
|
||||||
private final DeferredLogFactory logFactory;
|
private final DeferredLogFactory logFactory;
|
||||||
|
|
||||||
private final Log logger;
|
private final Log logger;
|
||||||
|
@ -212,7 +224,9 @@ class ConfigDataEnvironment {
|
||||||
this.logger.trace("Deducing profiles from current config data environment contributors");
|
this.logger.trace("Deducing profiles from current config data environment contributors");
|
||||||
Binder binder = contributors.getBinder(activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
|
Binder binder = contributors.getBinder(activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
|
||||||
try {
|
try {
|
||||||
Profiles profiles = new Profiles(this.environment, binder, this.additionalProfiles);
|
Set<String> additionalProfiles = new LinkedHashSet<>(this.additionalProfiles);
|
||||||
|
additionalProfiles.addAll(getIncludedProfiles(contributors, activationContext));
|
||||||
|
Profiles profiles = new Profiles(this.environment, binder, additionalProfiles);
|
||||||
return activationContext.withProfiles(profiles);
|
return activationContext.withProfiles(profiles);
|
||||||
}
|
}
|
||||||
catch (BindException ex) {
|
catch (BindException ex) {
|
||||||
|
@ -223,6 +237,27 @@ class ConfigDataEnvironment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Collection<? extends String> getIncludedProfiles(ConfigDataEnvironmentContributors contributors,
|
||||||
|
ConfigDataActivationContext activationContext) {
|
||||||
|
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
|
||||||
|
contributors, activationContext, true);
|
||||||
|
Set<String> result = new LinkedHashSet<>();
|
||||||
|
for (ConfigDataEnvironmentContributor contributor : contributors) {
|
||||||
|
ConfigurationPropertySource source = contributor.getConfigurationPropertySource();
|
||||||
|
if (source == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Binder binder = new Binder(Collections.singleton(source), placeholdersResolver);
|
||||||
|
binder.bind(INCLUDE_PROFILES, STRING_LIST).ifBound((includes) -> {
|
||||||
|
if (!contributor.isActive(activationContext)) {
|
||||||
|
InactiveConfigDataAccessException.throwIfPropertyFound(contributor, INCLUDE_PROFILES);
|
||||||
|
}
|
||||||
|
result.addAll(includes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,
|
private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,
|
||||||
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
|
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
|
||||||
this.logger.trace("Processing config data environment contributors with profile activation context");
|
this.logger.trace("Processing config data environment contributors with profile activation context");
|
||||||
|
|
|
@ -35,18 +35,12 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS
|
||||||
*/
|
*/
|
||||||
public class InvalidConfigDataPropertyException extends ConfigDataException {
|
public class InvalidConfigDataPropertyException extends ConfigDataException {
|
||||||
|
|
||||||
private static final Map<ConfigurationPropertyName, ConfigurationPropertyName> ERROR;
|
|
||||||
|
|
||||||
private static final Map<ConfigurationPropertyName, ConfigurationPropertyName> WARNING;
|
private static final Map<ConfigurationPropertyName, ConfigurationPropertyName> WARNING;
|
||||||
static {
|
static {
|
||||||
Map<ConfigurationPropertyName, ConfigurationPropertyName> warning = new LinkedHashMap<>();
|
Map<ConfigurationPropertyName, ConfigurationPropertyName> warning = new LinkedHashMap<>();
|
||||||
warning.put(ConfigurationPropertyName.of("spring.profiles"),
|
warning.put(ConfigurationPropertyName.of("spring.profiles"),
|
||||||
ConfigurationPropertyName.of("spring.config.activate.on-profile"));
|
ConfigurationPropertyName.of("spring.config.activate.on-profile"));
|
||||||
WARNING = Collections.unmodifiableMap(warning);
|
WARNING = Collections.unmodifiableMap(warning);
|
||||||
Map<ConfigurationPropertyName, ConfigurationPropertyName> error = new LinkedHashMap<>();
|
|
||||||
error.put(ConfigurationPropertyName.of("spring.profiles.include"),
|
|
||||||
ConfigurationPropertyName.of("spring.profiles.group"));
|
|
||||||
ERROR = Collections.unmodifiableMap(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ConfigurationProperty property;
|
private final ConfigurationProperty property;
|
||||||
|
@ -90,20 +84,16 @@ public class InvalidConfigDataPropertyException extends ConfigDataException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw a {@link InvalidConfigDataPropertyException} if the given
|
* Throw a {@link InvalidConfigDataPropertyException} or log a warning if the given
|
||||||
* {@link ConfigDataEnvironmentContributor} contains any invalid property.
|
* {@link ConfigDataEnvironmentContributor} contains any invalid property. A warning
|
||||||
|
* is logged if the property is still supported, but not recommended. An error is
|
||||||
|
* thrown if the property is completely unsupported.
|
||||||
* @param logger the logger to use for warnings
|
* @param logger the logger to use for warnings
|
||||||
* @param contributor the contributor to check
|
* @param contributor the contributor to check
|
||||||
*/
|
*/
|
||||||
static void throwOrWarn(Log logger, ConfigDataEnvironmentContributor contributor) {
|
static void throwOrWarn(Log logger, ConfigDataEnvironmentContributor contributor) {
|
||||||
ConfigurationPropertySource propertySource = contributor.getConfigurationPropertySource();
|
ConfigurationPropertySource propertySource = contributor.getConfigurationPropertySource();
|
||||||
if (propertySource != null) {
|
if (propertySource != null) {
|
||||||
ERROR.forEach((invalid, replacement) -> {
|
|
||||||
ConfigurationProperty property = propertySource.getConfigurationProperty(invalid);
|
|
||||||
if (property != null) {
|
|
||||||
throw new InvalidConfigDataPropertyException(property, replacement, contributor.getLocation());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
WARNING.forEach((invalid, replacement) -> {
|
WARNING.forEach((invalid, replacement) -> {
|
||||||
ConfigurationProperty property = propertySource.getConfigurationProperty(invalid);
|
ConfigurationProperty property = propertySource.getConfigurationProperty(invalid);
|
||||||
if (property != null) {
|
if (property != null) {
|
||||||
|
|
|
@ -49,6 +49,11 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
public class Profiles implements Iterable<String> {
|
public class Profiles implements Iterable<String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of property to set to specify additionally included active profiles.
|
||||||
|
*/
|
||||||
|
public static final String INCLUDE_PROFILES_PROPERTY_NAME = "spring.profiles.include";
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
|
@ -67,7 +72,7 @@ public class Profiles implements Iterable<String> {
|
||||||
* {@link Binder}.
|
* {@link Binder}.
|
||||||
* @param environment the source environment
|
* @param environment the source environment
|
||||||
* @param binder the binder for profile properties
|
* @param binder the binder for profile properties
|
||||||
* @param additionalProfiles and additional active profiles
|
* @param additionalProfiles any additional active profiles
|
||||||
*/
|
*/
|
||||||
Profiles(Environment environment, Binder binder, Collection<String> additionalProfiles) {
|
Profiles(Environment environment, Binder binder, Collection<String> additionalProfiles) {
|
||||||
this.groups = binder.bind("spring.profiles.group", STRING_STRINGS_MAP).orElseGet(LinkedMultiValueMap::new);
|
this.groups = binder.bind("spring.profiles.group", STRING_STRINGS_MAP).orElseGet(LinkedMultiValueMap::new);
|
||||||
|
|
|
@ -549,6 +549,28 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
|
||||||
() -> this.application.run("--spring.config.location=classpath:missing-appplication.properties"));
|
() -> this.application.run("--spring.config.location=classpath:missing-appplication.properties"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void runWhenHasIncludedProfilesActivatesProfiles() {
|
||||||
|
ConfigurableApplicationContext context = this.application
|
||||||
|
.run("--spring.config.location=classpath:application-include-profiles.properties");
|
||||||
|
assertThat(context.getEnvironment().getActiveProfiles()).containsExactlyInAnyOrder("p1", "p2", "p3", "p4",
|
||||||
|
"p5");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void runWhenHasIncludedProfilesWithPlaceholderActivatesProfiles() {
|
||||||
|
ConfigurableApplicationContext context = this.application
|
||||||
|
.run("--spring.config.location=classpath:application-include-profiles-with-placeholder.properties");
|
||||||
|
assertThat(context.getEnvironment().getActiveProfiles()).containsExactlyInAnyOrder("p1", "p2", "p3", "p4",
|
||||||
|
"p5");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void runWhenHasIncludedProfilesWithProfileSpecificDocumentThrowsException() {
|
||||||
|
assertThatExceptionOfType(InactiveConfigDataAccessException.class).isThrownBy(() -> this.application
|
||||||
|
.run("--spring.config.location=classpath:application-include-profiles-in-profile-specific.properties"));
|
||||||
|
}
|
||||||
|
|
||||||
private Condition<ConfigurableEnvironment> matchingPropertySource(final String sourceName) {
|
private Condition<ConfigurableEnvironment> matchingPropertySource(final String sourceName) {
|
||||||
return new Condition<ConfigurableEnvironment>("environment containing property source " + sourceName) {
|
return new Condition<ConfigurableEnvironment>("environment containing property source " + sourceName) {
|
||||||
|
|
||||||
|
|
|
@ -123,17 +123,6 @@ class InvalidConfigDataPropertyExceptionTests {
|
||||||
+ "'spring.config.activate.on-profile' [origin: \"spring.profiles\" from property source \"mockProperties\"]");
|
+ "'spring.config.activate.on-profile' [origin: \"spring.profiles\" from property source \"mockProperties\"]");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void throwOrWarnWhenHasErrorPropertyThrowsException() {
|
|
||||||
MockPropertySource propertySource = new MockPropertySource();
|
|
||||||
propertySource.setProperty("spring.profiles.include", "a");
|
|
||||||
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
|
|
||||||
assertThatExceptionOfType(InvalidConfigDataPropertyException.class)
|
|
||||||
.isThrownBy(() -> InvalidConfigDataPropertyException.throwOrWarn(this.logger, contributor))
|
|
||||||
.withMessage("Property 'spring.profiles.include' is invalid and should be replaced with "
|
|
||||||
+ "'spring.profiles.group' [origin: \"spring.profiles.include\" from property source \"mockProperties\"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TestConfigDataLocation extends ConfigDataLocation {
|
private static class TestConfigDataLocation extends ConfigDataLocation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
spring.profiles.active=p1
|
||||||
|
spring.profiles.include=p2
|
||||||
|
#---
|
||||||
|
spring.config.activate.on-profile=p2
|
||||||
|
spring.profiles.include=p3
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
spring.profiles.active=p1
|
||||||
|
spring.profiles.include=p2
|
||||||
|
#---
|
||||||
|
myprofile=p4
|
||||||
|
spring.profiles.include=p3,${myprofile}
|
||||||
|
#---
|
||||||
|
myotherprofile=p5
|
||||||
|
spring.profiles.include=${myotherprofile}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
spring.profiles.active=p1
|
||||||
|
spring.profiles.include=p2
|
||||||
|
#---
|
||||||
|
spring.profiles.include=p3,p4
|
||||||
|
#---
|
||||||
|
spring.profiles.include=p5
|
||||||
|
|
Loading…
Reference in New Issue