Browse Source

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
pull/1239/head
Ryan Baxter 1 year ago committed by GitHub
parent
commit
2f467e5823
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      docs/src/main/asciidoc/_configprops.adoc
  2. 10
      docs/src/main/asciidoc/spring-cloud-commons.adoc
  3. 7
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java
  4. 121
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapConfiguration.java
  5. 13
      spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapProperties.java
  6. 393
      spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java

1
docs/src/main/asciidoc/_configprops.adoc

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
|spring.cloud.compatibility-verifier.compatible-boot-versions | | Default accepted versions for the Spring Boot dependency. You can set {@code x} for the patch version if you don't want to specify a concrete value. Example: {@code 3.4.x}
|spring.cloud.compatibility-verifier.enabled | `+++false+++` | Enables creation of Spring Cloud compatibility verification.
|spring.cloud.config.allow-override | `+++true+++` | Flag to indicate that {@link #isOverrideSystemProperties() systemPropertiesOverride} can be used. Set to false to prevent users from changing the default accidentally. Default true.
|spring.cloud.config.initialize-on-context-refresh | `+++false+++` | Flag to initialize bootstrap configuration on context refresh event. Default false.
|spring.cloud.config.override-none | `+++false+++` | Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is true, external properties should take lowest priority and should not override any existing property sources (including local config files). Default false.
|spring.cloud.config.override-system-properties | `+++true+++` | Flag to indicate that the external properties should override system properties. Default true.
|spring.cloud.decrypt-environment-post-processor.enabled | `+++true+++` | Enable the DecryptEnvironmentPostProcessor.

10
docs/src/main/asciidoc/spring-cloud-commons.adoc

@ -54,6 +54,10 @@ The additional property sources are: @@ -54,6 +54,10 @@ The additional property sources are:
An example would be properties from the Spring Cloud Config Server.
See "`<<customizing-bootstrap-property-sources>>`" for how to customize the contents of this property source.
NOTE: Prior to Spring Cloud 2021.0.7 `PropertySourceLocators` (including the ones for Spring Cloud Config) were run during
the main application context and not in the Bootstrap context. You can force `PropertySourceLocators` to be run during the
Bootstrap context by setting `spring.cloud.config.initialize-on-context-refresh=true` in `bootstrap.[properties | yaml]`.
* "`applicationConfig: [classpath:bootstrap.yml]`" (and related files if Spring profiles are active): If you have a `bootstrap.yml` (or `.properties`), those properties are used to configure the bootstrap context.
Then they get added to the child context when its parent is set.
They have lower precedence than the `application.yml` (or `.properties`) and any other property sources that are added to the child as a normal part of the process of creating a Spring Boot application.
@ -146,6 +150,12 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomP @@ -146,6 +150,12 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomP
----
====
As of Spring Cloud 2021.0.8, Spring Cloud will now call `PropertySourceLocators` twice. The first fetch
will retrieve any property sources without any profiles. These property sources will have the opportunity to
activate profiles using `spring.profiles.active`. After the main application context starts `PropertySourceLocators`
will be called a second time, this time with any active profiles allowing `PropertySourceLocators` to locate
any additional `PropertySources` with profiles.
=== Logging Configuration
If you use Spring Boot to configure log settings, you should place this configuration in `bootstrap.[yml | properties]` if you would like it to apply to all events.

7
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java

@ -294,6 +294,13 @@ public class BootstrapApplicationListener implements ApplicationListener<Applica @@ -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")

121
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapConfiguration.java

@ -17,18 +17,20 @@ @@ -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; @@ -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; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -210,43 +240,98 @@ public class PropertySourceBootstrapConfiguration
return environment;
}
private void handleIncludedProfiles(ConfigurableEnvironment environment) {
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<>();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
addIncludedProfilesTo(includeProfiles, propertySource);
addIncludedProfilesTo(includeProfiles, propertySource, environment);
addActiveProfilesTo(activeProfiles, propertySource, environment);
}
List<String> activeProfiles = new ArrayList<>();
Collections.addAll(activeProfiles, environment.getActiveProfiles());
// 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());
}
}
}
}

13
spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapProperties.java

@ -46,6 +46,19 @@ public class PropertySourceBootstrapProperties { @@ -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;
}

393
spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java

@ -32,10 +32,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);
}

Loading…
Cancel
Save