Merge branch '3.1.x'

This commit is contained in:
Ryan Baxter
2023-05-04 15:26:49 -04:00
6 changed files with 476 additions and 72 deletions

View File

@@ -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.

View File

@@ -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
----
====
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.

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,17 +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.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 +42,10 @@ import org.springframework.cloud.bootstrap.BootstrapConfigFileApplicationListene
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 +57,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 +66,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 +82,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 +94,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;
@@ -121,7 +147,7 @@ public class PropertySourceBootstrapConfiguration
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment);
setLogLevels(applicationContext, environment);
handleIncludedProfiles(environment);
handleProfiles(environment);
}
}
@@ -170,7 +196,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;
}
@@ -208,43 +239,100 @@ 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(BootstrapConfigFileApplicationListener.INCLUDE_PROFILES_PROPERTY)));
propertySource.getProperty(BootstrapConfigFileApplicationListener.INCLUDE_PROFILES_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,9 +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.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;
@@ -76,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.cloud.bootstrap.enabled=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()
@@ -91,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.cloud.bootstrap.enabled=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()
@@ -105,9 +127,18 @@ public class BootstrapConfigurationTests {
@Test
public void bootstrapPropertiesAvailableInInitializer() {
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.cloud.bootstrap.enabled=true").sources(BareConfiguration.class)
.initializers(applicationContext -> {
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();
@@ -128,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.cloud.bootstrap.enabled=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"))
@@ -139,34 +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.cloud.bootstrap.enabled=true").sources(BareConfiguration.class).run());
Throwable throwable = Assertions.assertThrows(RuntimeException.class, () -> {
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.cloud.bootstrap.enabled=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.cloud.bootstrap.enabled=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
@@ -174,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.cloud.bootstrap.enabled=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.cloud.bootstrap.enabled=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.singletonMap("bootstrap.foo", "splat")));
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.properties("spring.cloud.bootstrap.enabled=true").environment(environment)
.sources(BareConfiguration.class).run();
environment.getPropertySources().addLast(
new MapPropertySource("last", Collections.<String, Object>singletonMap("bootstrap.foo", "splat")));
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.cloud.bootstrap.enabled=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
@@ -223,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.cloud.bootstrap.enabled=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
@@ -236,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.cloud.bootstrap.enabled=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)
@@ -251,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.cloud.bootstrap.enabled=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();
@@ -266,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.cloud.bootstrap.enabled=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");
@@ -280,9 +431,18 @@ public class BootstrapConfigurationTests {
@Test
public void listOverride() {
this.context = new SpringApplicationBuilder().sources(BareConfiguration.class)
.properties("spring.cloud.bootstrap.enabled=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);
@@ -291,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.cloud.bootstrap.enabled=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")
@@ -314,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.cloud.bootstrap.enabled=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()
@@ -336,7 +515,7 @@ public class BootstrapConfigurationTests {
ConfigurableApplicationContext parent = new SpringApplicationBuilder().sources(BareConfiguration.class)
.profiles("parent").web(WebApplicationType.NONE).run();
this.context = new SpringApplicationBuilder(BareConfiguration.class)
.properties("spring.cloud.bootstrap.enabled=true").profiles("child").parent(parent)
.properties("spring.config.use-legacy-processing=true").profiles("child").parent(parent)
.web(WebApplicationType.NONE).run();
then(this.context.getParent().getEnvironment()).isNotSameAs(this.context.getEnvironment());
// The ApplicationContext merges profiles (profiles and property sources), see
@@ -360,26 +539,120 @@ 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.cloud.bootstrap.enabled=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");
then(this.context.getEnvironment().getActiveProfiles()).contains("foo");
}
@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");
then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo");
}
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();
}
@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.cloud.bootstrap.enabled=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.cloud.bootstrap.enabled=true", "spring.cloud.bootstrap.name=nonenumerable").run();
.properties(properties).run();
then(this.context.getEnvironment().getProperty("foo")).isEqualTo("bar");
}
@@ -419,12 +692,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);
}