Add application listener to locate property sources during bootstrap (#1228)

* Add application listener to locate property sources during bootstrap

Also adds support for activating profiles using spring.profiles.active from bootstrap property source listeners.

Allow profiles to be passed from bootstrap context to main application context

* Add application listener to locate property sources during bootstrap

Also adds support for activating profiles using spring.profiles.active from bootstrap property source listeners.

Allow profiles to be passed from bootstrap context to main application context

Updating version in docs
This commit is contained in:
Ryan Baxter
2023-05-04 15:06:20 -04:00
committed by GitHub
parent c9aaa5fdb6
commit 2f467e5823
6 changed files with 470 additions and 77 deletions

View File

@@ -294,6 +294,13 @@ public class BootstrapApplicationListener implements ApplicationListener<Applica
target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
application.setInitializers(target);
addBootstrapDecryptInitializer(application);
// Get the active profiles from the bootstrap context and set them in main
// application
// environment. This allows any profiles activated during bootstrap to be
// activated when
// config data runs in the main application context.
environment.setActiveProfiles(context.getEnvironment().getActiveProfiles());
}
@SuppressWarnings("unchecked")

View File

@@ -17,18 +17,20 @@
package org.springframework.cloud.bootstrap.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
@@ -39,8 +41,10 @@ import org.springframework.cloud.bootstrap.BootstrapApplicationListener;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.logging.LoggingRebinder;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.AbstractEnvironment;
@@ -52,6 +56,7 @@ import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.bootstrap.encrypt.AbstractEnvironmentDecrypt.DECRYPTED_PROPERTY_SOURCE_NAME;
import static org.springframework.core.env.StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
/**
@@ -60,8 +65,8 @@ import static org.springframework.core.env.StandardEnvironment.SYSTEM_ENVIRONMEN
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
public class PropertySourceBootstrapConfiguration implements ApplicationListener<ContextRefreshedEvent>,
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
/**
* Bootstrap property source name.
@@ -76,6 +81,9 @@ public class PropertySourceBootstrapConfiguration
@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
@Autowired
private PropertySourceBootstrapProperties bootstrapProperties;
@Override
public int getOrder() {
return this.order;
@@ -85,8 +93,25 @@ public class PropertySourceBootstrapConfiguration
this.propertySourceLocators = new ArrayList<>(propertySourceLocators);
}
/*
* The ApplicationListener is called when the main application context is initialized.
* This will be called after the ApplicationListener ContextRefreshedEvent is fired
* during the bootstrap phase. This method is also what added PropertySources prior to
* Spring Cloud 2021.0.7, this is why it will be called when
* spring.cloud.config.initialize-on-context-refresh is false. When
* spring.cloud.config.initialize-on-context-refresh is true this method provides a
* "second fetch" of configuration data to fetch any additional configuration data
* from profiles that have been activated.
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
if (!bootstrapProperties.isInitializeOnContextRefresh() || !applicationContext.getEnvironment()
.getPropertySources().contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
doInitialize(applicationContext);
}
}
private void doInitialize(ConfigurableApplicationContext applicationContext) {
List<PropertySource<?>> composite = new ArrayList<>();
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
@@ -122,7 +147,7 @@ public class PropertySourceBootstrapConfiguration
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment, logConfig, logFile);
setLogLevels(applicationContext, environment);
handleIncludedProfiles(environment);
handleProfiles(environment);
}
}
@@ -172,7 +197,12 @@ public class PropertySourceBootstrapConfiguration
if (!remoteProperties.isAllowOverride()
|| (!remoteProperties.isOverrideNone() && remoteProperties.isOverrideSystemProperties())) {
for (PropertySource<?> p : reversedComposite) {
propertySources.addFirst(p);
if (propertySources.contains(DECRYPTED_PROPERTY_SOURCE_NAME)) {
propertySources.addAfter(DECRYPTED_PROPERTY_SOURCE_NAME, p);
}
else {
propertySources.addFirst(p);
}
}
return;
}
@@ -210,43 +240,98 @@ public class PropertySourceBootstrapConfiguration
return environment;
}
private void handleIncludedProfiles(ConfigurableEnvironment environment) {
Set<String> includeProfiles = new TreeSet<>();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
addIncludedProfilesTo(includeProfiles, propertySource);
private void handleProfiles(ConfigurableEnvironment environment) {
if (bootstrapProperties.isInitializeOnContextRefresh() && !environment.getPropertySources()
.contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
// In the case that spring.cloud.config.initialize-on-context-refresh is true
// this method will
// be called during the bootstrap phase and the main application startup. We
// only manipulate the environment profiles in the bootstrap phase as we are
// fetching
// any additional profile specific configuration when this method would be
// called during the
// main application startup, and it is not valid to activate profiles in
// profile specific
// configuration properties, so we should not run this method then.
return;
}
Set<String> includeProfiles = new TreeSet<>();
List<String> activeProfiles = new ArrayList<>();
Collections.addAll(activeProfiles, environment.getActiveProfiles());
for (PropertySource<?> propertySource : environment.getPropertySources()) {
addIncludedProfilesTo(includeProfiles, propertySource, environment);
addActiveProfilesTo(activeProfiles, propertySource, environment);
}
// If it's already accepted we assume the order was set intentionally
includeProfiles.removeAll(activeProfiles);
if (includeProfiles.isEmpty()) {
return;
}
// Prepend each added profile (last wins in a property key clash)
for (String profile : includeProfiles) {
activeProfiles.add(0, profile);
}
List<String> activeProfilesFromEnvironment = Arrays.stream(environment.getActiveProfiles())
.collect(Collectors.toList());
if (!activeProfiles.containsAll(activeProfilesFromEnvironment)) {
activeProfiles.addAll(activeProfilesFromEnvironment);
}
environment.setActiveProfiles(activeProfiles.toArray(new String[activeProfiles.size()]));
}
private Set<String> addIncludedProfilesTo(Set<String> profiles, PropertySource<?> propertySource) {
private Set<String> addIncludedProfilesTo(Set<String> profiles, PropertySource<?> propertySource,
ConfigurableEnvironment environment) {
return addProfilesTo(profiles, propertySource, Profiles.INCLUDE_PROFILES_PROPERTY_NAME, environment);
}
private List<String> addActiveProfilesTo(List<String> profiles, PropertySource<?> propertySource,
ConfigurableEnvironment environment) {
return addProfilesTo(profiles, propertySource, AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, environment);
}
private <T extends Collection<String>> T addProfilesTo(T profiles, PropertySource<?> propertySource,
String property, ConfigurableEnvironment environment) {
if (propertySource instanceof CompositePropertySource) {
for (PropertySource<?> nestedPropertySource : ((CompositePropertySource) propertySource)
.getPropertySources()) {
addIncludedProfilesTo(profiles, nestedPropertySource);
addProfilesTo(profiles, nestedPropertySource, property, environment);
}
}
else {
Collections.addAll(profiles, getProfilesForValue(
propertySource.getProperty(ConfigFileApplicationListener.INCLUDE_PROFILES_PROPERTY)));
Collections.addAll(profiles, getProfilesForValue(propertySource.getProperty(property), environment));
}
return profiles;
}
private String[] getProfilesForValue(Object property) {
private String[] getProfilesForValue(Object property, ConfigurableEnvironment environment) {
final String value = (property == null ? null : property.toString());
return property == null ? new String[0] : StringUtils.tokenizeToStringArray(value, ",");
return property == null ? new String[0] : resolvePlaceholdersInProfiles(value, environment);
}
private String[] resolvePlaceholdersInProfiles(String profiles, ConfigurableEnvironment environment) {
return Arrays.stream(StringUtils.tokenizeToStringArray(profiles, ",")).map(s -> {
if (s.startsWith("${") && s.endsWith("}")) {
return environment.resolvePlaceholders(s);
}
else {
return s;
}
}).toArray(String[]::new);
}
/*
* The ConextRefreshedEvent gets called at the end of the boostrap phase after config
* data is loaded during bootstrap. This will run and do an "initial fetch" of
* configuration data during bootstrap but before the main applicaiton context starts.
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (bootstrapProperties.isInitializeOnContextRefresh()
&& event.getApplicationContext() instanceof ConfigurableApplicationContext) {
if (((ConfigurableApplicationContext) event.getApplicationContext()).getEnvironment().getPropertySources()
.contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
doInitialize((ConfigurableApplicationContext) event.getApplicationContext());
}
}
}
}

View File

@@ -46,6 +46,19 @@ public class PropertySourceBootstrapProperties {
*/
private boolean overrideNone = false;
/**
* Flag to initialize bootstrap configuration on context refresh event. Default false.
*/
private boolean initializeOnContextRefresh = false;
public boolean isInitializeOnContextRefresh() {
return initializeOnContextRefresh;
}
public void setInitializeOnContextRefresh(boolean initializeOnContextRefresh) {
this.initializeOnContextRefresh = initializeOnContextRefresh;
}
public boolean isOverrideNone() {
return this.overrideNone;
}

View File

@@ -32,10 +32,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.bootstrap.BootstrapApplicationListener;
import org.springframework.cloud.bootstrap.TestHigherPriorityBootstrapConfiguration;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
@@ -77,11 +78,20 @@ public class BootstrapConfigurationTests {
@Test
public void pickupOnlyExternalBootstrapProperties() {
String externalPropertiesPath = getExternalProperties();
pickupOnlyExternalBootstrapProperties("spring.cloud.bootstrap.location=" + externalPropertiesPath,
"spring.config.use-legacy-processing=true");
}
@Test
public void pickupOnlyExternalBootstrapPropertiesWithAppListener() {
String externalPropertiesPath = getExternalProperties();
pickupOnlyExternalBootstrapProperties("spring.cloud.bootstrap.location=" + externalPropertiesPath,
"spring.config.use-legacy-processing=true", "spring.cloud.config.initialize-on-context-refresh=true");
}
private void pickupOnlyExternalBootstrapProperties(String... properties) {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(BareConfiguration.class)
.properties("spring.cloud.bootstrap.location=" + externalPropertiesPath,
"spring.config.use-legacy-processing=true")
.run();
.properties(properties).run();
then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName");
then(this.context.getEnvironment().getProperty("info.desc")).isNull();
then(this.context.getEnvironment().getPropertySources()
@@ -92,11 +102,22 @@ public class BootstrapConfigurationTests {
@Test
public void pickupAdditionalExternalBootstrapProperties() {
String externalPropertiesPath = getExternalProperties();
pickupAdditionalExternalBootstrapProperties(
"spring.cloud.bootstrap.additional-location=" + externalPropertiesPath,
"spring.config.use-legacy-processing=true");
}
@Test
public void pickupAdditionalExternalBootstrapPropertiesWithAppListener() {
String externalPropertiesPath = getExternalProperties();
pickupAdditionalExternalBootstrapProperties(
"spring.cloud.bootstrap.additional-location=" + externalPropertiesPath,
"spring.config.use-legacy-processing=true", "spring.cloud.config.initialize-on-context-refresh=true");
}
private void pickupAdditionalExternalBootstrapProperties(String... properties) {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(BareConfiguration.class)
.properties("spring.cloud.bootstrap.additional-location=" + externalPropertiesPath,
"spring.config.use-legacy-processing=true")
.run();
.properties(properties).run();
then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName");
then(this.context.getEnvironment().getProperty("info.desc")).isEqualTo("defaultPropertiesInfoDesc");
then(this.context.getEnvironment().getPropertySources()
@@ -106,14 +127,20 @@ public class BootstrapConfigurationTests {
@Test
public void bootstrapPropertiesAvailableInInitializer() {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class)
.initializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// This property is defined in bootstrap.properties
then(applicationContext.getEnvironment().getProperty("info.name")).isEqualTo("child");
}
bootstrapPropertiesAvailableInInitializer("spring.config.use-legacy-processing=true");
}
@Test
public void bootstrapPropertiesAvailableInInitializerWithAppContext() {
bootstrapPropertiesAvailableInInitializer("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void bootstrapPropertiesAvailableInInitializer(String... properties) {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).initializers(applicationContext -> {
// This property is defined in bootstrap.properties
then(applicationContext.getEnvironment().getProperty("info.name")).isEqualTo("child");
}).run();
then(this.context.getEnvironment().getPropertySources()
.contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap"))
@@ -132,9 +159,19 @@ public class BootstrapConfigurationTests {
@Test
public void picksUpAdditionalPropertySource() {
picksUpAdditionalPropertySource("spring.config.use-legacy-processing=true");
}
@Test
public void picksUpAdditionalPropertySourceWithAppContext() {
picksUpAdditionalPropertySource("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void picksUpAdditionalPropertySource(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar");
then(this.context.getEnvironment().getPropertySources()
.contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap"))
@@ -143,35 +180,77 @@ public class BootstrapConfigurationTests {
@Test
public void failsOnPropertySource() {
failsOnPropertySource("spring.config.use-legacy-processing=true");
}
@Test
public void failsOnPropertySourceWithAppContext() {
failsOnPropertySource("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void failsOnPropertySource(String... properties) {
System.setProperty("expected.fail", "true");
Throwable throwable = Assertions.assertThrows(RuntimeException.class, () -> {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
});
then(throwable.getMessage().equals("Planned"));
}
@Test
public void overrideSystemPropertySourceByDefault() {
overrideSystemPropertySourceByDefault("spring.config.use-legacy-processing=true");
}
@Test
public void overrideSystemPropertySourceByDefaultWithAppContext() {
overrideSystemPropertySourceByDefault("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void overrideSystemPropertySourceByDefault(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
System.setProperty("bootstrap.foo", "system");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar");
}
@Test
public void systemPropertyOverrideFalse() {
systemPropertyOverrideFalse("spring.config.use-legacy-processing=true");
}
@Test
public void systemPropertyOverrideFalseWithAppContext() {
systemPropertyOverrideFalse("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void systemPropertyOverrideFalse(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false");
System.setProperty("bootstrap.foo", "system");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system");
}
@Test
public void systemPropertyOverrideWhenOverrideDisallowed() {
systemPropertyOverrideWhenOverrideDisallowed("spring.config.use-legacy-processing=true");
}
@Test
public void systemPropertyOverrideWhenOverrideDisallowedWithAppContext() {
systemPropertyOverrideWhenOverrideDisallowed("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void systemPropertyOverrideWhenOverrideDisallowed(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false");
// If spring.cloud.config.allowOverride=false is in the remote property sources
@@ -179,42 +258,71 @@ public class BootstrapConfigurationTests {
// their own remote property source.
PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "false");
System.setProperty("bootstrap.foo", "system");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar");
}
@Test
public void systemPropertyOverrideFalseWhenOverrideAllowed() {
systemPropertyOverrideFalseWhenOverrideAllowed("spring.config.use-legacy-processing=true");
}
@Test
public void systemPropertyOverrideFalseWhenOverrideAllowedWithAppContext() {
systemPropertyOverrideFalseWhenOverrideAllowed("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void systemPropertyOverrideFalseWhenOverrideAllowed(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false");
PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true");
System.setProperty("bootstrap.foo", "system");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system");
}
@Test
public void overrideAllWhenOverrideAllowed() {
overrideAllWhenOverrideAllowed("spring.config.use-legacy-processing=true");
}
@Test
public void overrideAllWhenOverrideAllowedWithAppContext() {
overrideAllWhenOverrideAllowed("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void overrideAllWhenOverrideAllowed(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideNone", "true");
PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true");
ConfigurableEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addLast(
new MapPropertySource("last", Collections.<String, Object>singletonMap("bootstrap.foo", "splat")));
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").environment(environment)
.sources(BareConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.environment(environment).sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("splat");
}
@Test
public void applicationNameInBootstrapAndMain() {
applicationNameInBootstrapAndMain("spring.cloud.bootstrap.name:other",
"spring.config.use-legacy-processing=true", "spring.config.name:plain");
}
@Test
public void applicationNameInBootstrapAndMainWithAppContext() {
applicationNameInBootstrapAndMain("spring.cloud.bootstrap.name:other",
"spring.config.use-legacy-processing=true", "spring.config.name:plain",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void applicationNameInBootstrapAndMain(String... properties) {
System.setProperty("expected.name", "main");
this.context = new SpringApplicationBuilder()
.web(WebApplicationType.NONE).properties("spring.cloud.bootstrap.name:other",
"spring.config.use-legacy-processing=true", "spring.config.name:plain")
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("app");
// The parent is called "main" because spring.application.name is specified in
@@ -228,10 +336,20 @@ public class BootstrapConfigurationTests {
@Test
public void applicationNameNotInBootstrap() {
applicationNameNotInBootstrap("spring.cloud.bootstrap.name:application",
"spring.config.use-legacy-processing=true", "spring.config.name:other");
}
@Test
public void applicationNameNotInBootstrapWithAppContext() {
applicationNameNotInBootstrap("spring.cloud.bootstrap.name:application",
"spring.config.use-legacy-processing=true", "spring.config.name:other",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void applicationNameNotInBootstrap(String... properties) {
System.setProperty("expected.name", "main");
this.context = new SpringApplicationBuilder()
.web(WebApplicationType.NONE).properties("spring.cloud.bootstrap.name:application",
"spring.config.use-legacy-processing=true", "spring.config.name:other")
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main");
// The parent has no name because spring.application.name is not
@@ -241,9 +359,18 @@ public class BootstrapConfigurationTests {
@Test
public void applicationNameOnlyInBootstrap() {
applicationNameOnlyInBootstrap("spring.cloud.bootstrap.name:other", "spring.config.use-legacy-processing=true");
}
@Test
public void applicationNameOnlyInBootstrapWithAppContext() {
applicationNameOnlyInBootstrap("spring.cloud.bootstrap.name:other", "spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void applicationNameOnlyInBootstrap(String... properties) {
System.setProperty("expected.name", "main");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.cloud.bootstrap.name:other", "spring.config.use-legacy-processing=true")
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.sources(BareConfiguration.class).run();
// The main context is called "main" because spring.application.name is specified
// in other.properties (and not in the main config file)
@@ -256,10 +383,20 @@ public class BootstrapConfigurationTests {
@Test
public void environmentEnrichedOnceWhenSharedWithChildContext() {
environmentEnrichedOnceWhenSharedWithChildContext("spring.config.use-legacy-processing=true");
}
@Test
public void environmentEnrichedOnceWhenSharedWithChildContextWithAppContext() {
environmentEnrichedOnceWhenSharedWithChildContext("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void environmentEnrichedOnceWhenSharedWithChildContext(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class)
.properties("spring.config.use-legacy-processing=true").environment(new StandardEnvironment())
.child(BareConfiguration.class).web(WebApplicationType.NONE).run();
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class).properties(properties)
.environment(new StandardEnvironment()).child(BareConfiguration.class).web(WebApplicationType.NONE)
.run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar");
then(this.context.getParent().getEnvironment()).isEqualTo(this.context.getEnvironment());
MutablePropertySources sources = this.context.getEnvironment().getPropertySources();
@@ -271,11 +408,20 @@ public class BootstrapConfigurationTests {
@Test
public void onlyOneBootstrapContext() {
onlyOneBootstrapContext("spring.config.use-legacy-processing=true");
}
@Test
public void onlyOneBootstrapContextWithAppContext() {
onlyOneBootstrapContext("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void onlyOneBootstrapContext(String... properties) {
TestHigherPriorityBootstrapConfiguration.count.set(0);
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class)
.properties("spring.config.use-legacy-processing=true").child(BareConfiguration.class)
.web(WebApplicationType.NONE).run();
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class).properties(properties)
.child(BareConfiguration.class).web(WebApplicationType.NONE).run();
then(TestHigherPriorityBootstrapConfiguration.count.get()).isEqualTo(1);
then(this.context.getParent()).isNotNull();
then(this.context.getParent().getParent().getId()).isEqualTo("bootstrap");
@@ -285,9 +431,18 @@ public class BootstrapConfigurationTests {
@Test
public void listOverride() {
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class)
.properties("spring.config.use-legacy-processing=true").child(BareConfiguration.class)
.web(WebApplicationType.NONE).run();
listOverride("spring.config.use-legacy-processing=true");
}
@Test
public void listOverrideWithAppContext() {
listOverride("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void listOverride(String... properties) {
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class).properties(properties)
.child(BareConfiguration.class).web(WebApplicationType.NONE).run();
ListProperties listProperties = new ListProperties();
Binder.get(this.context.getEnvironment()).bind("list", Bindable.ofInstance(listProperties));
then(listProperties.getFoo().size()).isEqualTo(1);
@@ -296,10 +451,20 @@ public class BootstrapConfigurationTests {
@Test
public void bootstrapContextSharedBySiblings() {
bootstrapContextSharedBySiblings("spring.config.use-legacy-processing=true");
}
@Test
public void bootstrapContextSharedBySiblingsWithAppContext() {
bootstrapContextSharedBySiblings("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void bootstrapContextSharedBySiblings(String... properties) {
TestHigherPriorityBootstrapConfiguration.count.set(0);
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
SpringApplicationBuilder builder = new SpringApplicationBuilder()
.properties("spring.config.use-legacy-processing=true").sources(BareConfiguration.class);
SpringApplicationBuilder builder = new SpringApplicationBuilder().properties(properties)
.sources(BareConfiguration.class);
this.sibling = builder.child(BareConfiguration.class).properties("spring.application.name=sibling")
.web(WebApplicationType.NONE).run();
this.context = builder.child(BareConfiguration.class).properties("spring.application.name=context")
@@ -319,10 +484,19 @@ public class BootstrapConfigurationTests {
@Test
public void environmentEnrichedInParentContext() {
environmentEnrichedInParentContext("spring.config.use-legacy-processing=true");
}
@Test
public void environmentEnrichedInParentContextWithAppContext() {
environmentEnrichedInParentContext("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void environmentEnrichedInParentContext(String... properties) {
PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar");
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class)
.properties("spring.config.use-legacy-processing=true").child(BareConfiguration.class)
.web(WebApplicationType.NONE).run();
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class).properties(properties)
.child(BareConfiguration.class).web(WebApplicationType.NONE).run();
then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar");
then(this.context.getParent().getEnvironment()).isNotSameAs(this.context.getEnvironment());
then(this.context.getEnvironment().getPropertySources()
@@ -365,27 +539,118 @@ public class BootstrapConfigurationTests {
@Test
public void includeProfileFromBootstrapPropertySource() {
includeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true");
}
@Test
public void includeProfileFromBootstrapPropertySourceWithAppContext() {
includeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void includeProfileFromBootstrapPropertySource(String... properties) {
PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.config.use-legacy-processing=true").profiles("foo").sources(BareConfiguration.class)
.run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.profiles("foo").sources(BareConfiguration.class).run();
then(this.context.getEnvironment().acceptsProfiles("baz")).isTrue();
then(this.context.getEnvironment().acceptsProfiles("bar")).isTrue();
}
@Test
public void activeProfileFromBootstrapPropertySource() {
activeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true");
}
@Test
public void activeProfileFromBootstrapPropertySourceWithAppContext() {
activeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
then(this.context.getEnvironment().getActiveProfiles()).doesNotContain("after");
}
private void activeProfileFromBootstrapPropertySource(String... properties) {
PropertySourceConfiguration.MAP.put("spring.profiles.active", "bar,baz");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.profiles("foo").sources(BareConfiguration.class).run();
then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "foo")).isTrue();
then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo");
}
@Test
public void activeAndIncludeProfileFromBootstrapPropertySource() {
activeAndIncludeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true");
}
@Test
public void activeAndIncludeProfileFromBootstrapPropertySourceWithAppContext() {
activeAndIncludeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true",
"spring.cloud.config.initialize-on-context-refresh=true");
}
private void activeAndIncludeProfileFromBootstrapPropertySource(String... properties) {
PropertySourceConfiguration.MAP.put("spring.profiles.active", "bar,baz");
PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz,hello");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.profiles("foo").sources(BareConfiguration.class).run();
then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue();
then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello");
}
@Test
public void activeAndIncludeProfileFromBootstrapPropertySourceWithReplacement() {
activeAndIncludeProfileFromBootstrapPropertySourceWithReplacement("spring.config.use-legacy-processing=true",
"barreplacement=bar");
}
@Test
public void activeAndIncludeProfileFromBootstrapPropertySourceWithReplacementWithAppContext() {
activeAndIncludeProfileFromBootstrapPropertySourceWithReplacement("spring.config.use-legacy-processing=true",
"barreplacement=bar", "spring.cloud.config.initialize-on-context-refresh=true");
}
private void activeAndIncludeProfileFromBootstrapPropertySourceWithReplacement(String... properties) {
PropertySourceConfiguration.MAP.put("spring.profiles.active", "${barreplacement},baz");
PropertySourceConfiguration.MAP.put("spring.profiles.include", "${barreplacement},baz,hello");
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).properties(properties)
.profiles("foo").sources(BareConfiguration.class).run();
then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue();
then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello");
}
@Test
public void includeProfileFromBootstrapProperties() {
includeProfileFromBootstrapProperties("spring.config.use-legacy-processing=true",
"spring.cloud.bootstrap.name=local");
}
@Test
public void includeProfileFromBootstrapPropertiesWithAppContext() {
includeProfileFromBootstrapProperties("spring.config.use-legacy-processing=true",
"spring.cloud.bootstrap.name=local", "spring.cloud.config.initialize-on-context-refresh=true");
}
private void includeProfileFromBootstrapProperties(String... properties) {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(BareConfiguration.class)
.properties("spring.config.use-legacy-processing=true", "spring.cloud.bootstrap.name=local").run();
.properties(properties).run();
then(this.context.getEnvironment().acceptsProfiles("local")).isTrue();
then(this.context.getEnvironment().getProperty("added")).isEqualTo("Hello added!");
}
@Test
public void nonEnumerablePropertySourceWorks() {
nonEnumerablePropertySourceWorks("spring.config.use-legacy-processing=true",
"spring.cloud.bootstrap.name=nonenumerable");
}
@Test
public void nonEnumerablePropertySourceWorksWithAppContext() {
nonEnumerablePropertySourceWorks("spring.config.use-legacy-processing=true",
"spring.cloud.bootstrap.name=nonenumerable", "spring.cloud.config.initialize-on-context-refresh=true");
}
private void nonEnumerablePropertySourceWorks(String... properties) {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(BareConfiguration.class)
.properties("spring.config.use-legacy-processing=true", "spring.cloud.bootstrap.name=nonenumerable")
.run();
.properties(properties).run();
then(this.context.getEnvironment().getProperty("foo")).isEqualTo("bar");
}
@@ -425,12 +690,24 @@ public class BootstrapConfigurationTests {
@Override
public PropertySource<?> locate(Environment environment) {
if (environment instanceof ConfigurableEnvironment) {
if (!((ConfigurableEnvironment) environment).getPropertySources()
.contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
if (MAP.containsKey(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)) {
// This additional profile, after, should not be added when
// initialize-on-context-refresh=true
MAP.put(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME,
MAP.get(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME) + ",after");
}
}
}
if (this.name != null) {
then(this.name).isEqualTo(environment.getProperty("spring.application.name"));
}
if (this.fail) {
throw new RuntimeException("Planned");
}
return new MapPropertySource("testBootstrap", MAP);
}