Skip to content

Commit ac709ec

Browse files
committed
Merge branch '3.1.x'
2 parents 81314d5 + 2f467e5 commit ac709ec

File tree

6 files changed

+475
-71
lines changed

6 files changed

+475
-71
lines changed

docs/src/main/asciidoc/_configprops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
|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}
55
|spring.cloud.compatibility-verifier.enabled | `+++false+++` | Enables creation of Spring Cloud compatibility verification.
66
|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.
7+
|spring.cloud.config.initialize-on-context-refresh | `+++false+++` | Flag to initialize bootstrap configuration on context refresh event. Default false.
78
|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.
89
|spring.cloud.config.override-system-properties | `+++true+++` | Flag to indicate that the external properties should override system properties. Default true.
910
|spring.cloud.decrypt-environment-post-processor.enabled | `+++true+++` | Enable the DecryptEnvironmentPostProcessor.

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ The additional property sources are:
5454
An example would be properties from the Spring Cloud Config Server.
5555
See "`<<customizing-bootstrap-property-sources>>`" for how to customize the contents of this property source.
5656

57+
NOTE: Prior to Spring Cloud 2021.0.7 `PropertySourceLocators` (including the ones for Spring Cloud Config) were run during
58+
the main application context and not in the Bootstrap context. You can force `PropertySourceLocators` to be run during the
59+
Bootstrap context by setting `spring.cloud.config.initialize-on-context-refresh=true` in `bootstrap.[properties | yaml]`.
60+
5761
* "`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.
5862
Then they get added to the child context when its parent is set.
5963
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
146150
----
147151
====
148152

153+
As of Spring Cloud 2021.0.8, Spring Cloud will now call `PropertySourceLocators` twice. The first fetch
154+
will retrieve any property sources without any profiles. These property sources will have the opportunity to
155+
activate profiles using `spring.profiles.active`. After the main application context starts `PropertySourceLocators`
156+
will be called a second time, this time with any active profiles allowing `PropertySourceLocators` to locate
157+
any additional `PropertySources` with profiles.
158+
149159
=== Logging Configuration
150160

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

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ private void apply(ConfigurableApplicationContext context, SpringApplication app
294294
target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
295295
application.setInitializers(target);
296296
addBootstrapDecryptInitializer(application);
297+
298+
// Get the active profiles from the bootstrap context and set them in main
299+
// application
300+
// environment. This allows any profiles activated during bootstrap to be
301+
// activated when
302+
// config data runs in the main application context.
303+
environment.setActiveProfiles(context.getEnvironment().getActiveProfiles());
297304
}
298305

299306
@SuppressWarnings("unchecked")

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

Lines changed: 104 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,20 @@
1717
package org.springframework.cloud.bootstrap.config;
1818

1919
import java.util.ArrayList;
20+
import java.util.Arrays;
2021
import java.util.Collection;
2122
import java.util.Collections;
2223
import java.util.List;
2324
import java.util.Map;
2425
import java.util.Set;
2526
import java.util.TreeSet;
27+
import java.util.stream.Collectors;
2628

2729
import org.apache.commons.logging.Log;
2830
import org.apache.commons.logging.LogFactory;
2931

3032
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.boot.context.config.Profiles;
3134
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3235
import org.springframework.boot.context.properties.bind.Bindable;
3336
import org.springframework.boot.context.properties.bind.Binder;
@@ -39,8 +42,10 @@
3942
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
4043
import org.springframework.cloud.logging.LoggingRebinder;
4144
import org.springframework.context.ApplicationContextInitializer;
45+
import org.springframework.context.ApplicationListener;
4246
import org.springframework.context.ConfigurableApplicationContext;
4347
import org.springframework.context.annotation.Configuration;
48+
import org.springframework.context.event.ContextRefreshedEvent;
4449
import org.springframework.core.Ordered;
4550
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
4651
import org.springframework.core.env.AbstractEnvironment;
@@ -52,6 +57,7 @@
5257
import org.springframework.core.env.PropertySource;
5358
import org.springframework.util.StringUtils;
5459

60+
import static org.springframework.cloud.bootstrap.encrypt.AbstractEnvironmentDecrypt.DECRYPTED_PROPERTY_SOURCE_NAME;
5561
import static org.springframework.core.env.StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
5662

5763
/**
@@ -60,8 +66,8 @@
6066
*/
6167
@Configuration(proxyBeanMethods = false)
6268
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
63-
public class PropertySourceBootstrapConfiguration
64-
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
69+
public class PropertySourceBootstrapConfiguration implements ApplicationListener<ContextRefreshedEvent>,
70+
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
6571

6672
/**
6773
* Bootstrap property source name.
@@ -76,6 +82,9 @@ public class PropertySourceBootstrapConfiguration
7682
@Autowired(required = false)
7783
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
7884

85+
@Autowired
86+
private PropertySourceBootstrapProperties bootstrapProperties;
87+
7988
@Override
8089
public int getOrder() {
8190
return this.order;
@@ -85,8 +94,25 @@ public void setPropertySourceLocators(Collection<PropertySourceLocator> property
8594
this.propertySourceLocators = new ArrayList<>(propertySourceLocators);
8695
}
8796

97+
/*
98+
* The ApplicationListener is called when the main application context is initialized.
99+
* This will be called after the ApplicationListener ContextRefreshedEvent is fired
100+
* during the bootstrap phase. This method is also what added PropertySources prior to
101+
* Spring Cloud 2021.0.7, this is why it will be called when
102+
* spring.cloud.config.initialize-on-context-refresh is false. When
103+
* spring.cloud.config.initialize-on-context-refresh is true this method provides a
104+
* "second fetch" of configuration data to fetch any additional configuration data
105+
* from profiles that have been activated.
106+
*/
88107
@Override
89108
public void initialize(ConfigurableApplicationContext applicationContext) {
109+
if (!bootstrapProperties.isInitializeOnContextRefresh() || !applicationContext.getEnvironment()
110+
.getPropertySources().contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
111+
doInitialize(applicationContext);
112+
}
113+
}
114+
115+
private void doInitialize(ConfigurableApplicationContext applicationContext) {
90116
List<PropertySource<?>> composite = new ArrayList<>();
91117
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
92118
boolean empty = true;
@@ -121,7 +147,7 @@ public void initialize(ConfigurableApplicationContext applicationContext) {
121147
insertPropertySources(propertySources, composite);
122148
reinitializeLoggingSystem(environment);
123149
setLogLevels(applicationContext, environment);
124-
handleIncludedProfiles(environment);
150+
handleProfiles(environment);
125151
}
126152
}
127153

@@ -170,7 +196,12 @@ private void insertPropertySources(MutablePropertySources propertySources, List<
170196
if (!remoteProperties.isAllowOverride()
171197
|| (!remoteProperties.isOverrideNone() && remoteProperties.isOverrideSystemProperties())) {
172198
for (PropertySource<?> p : reversedComposite) {
173-
propertySources.addFirst(p);
199+
if (propertySources.contains(DECRYPTED_PROPERTY_SOURCE_NAME)) {
200+
propertySources.addAfter(DECRYPTED_PROPERTY_SOURCE_NAME, p);
201+
}
202+
else {
203+
propertySources.addFirst(p);
204+
}
174205
}
175206
return;
176207
}
@@ -208,43 +239,100 @@ private Environment environment(MutablePropertySources incoming) {
208239
return environment;
209240
}
210241

211-
private void handleIncludedProfiles(ConfigurableEnvironment environment) {
242+
private void handleProfiles(ConfigurableEnvironment environment) {
243+
if (bootstrapProperties.isInitializeOnContextRefresh() && !environment.getPropertySources()
244+
.contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
245+
// In the case that spring.cloud.config.initialize-on-context-refresh is true
246+
// this method will
247+
// be called during the bootstrap phase and the main application startup. We
248+
// only manipulate the environment profiles in the bootstrap phase as we are
249+
// fetching
250+
// any additional profile specific configuration when this method would be
251+
// called during the
252+
// main application startup, and it is not valid to activate profiles in
253+
// profile specific
254+
// configuration properties, so we should not run this method then.
255+
return;
256+
}
212257
Set<String> includeProfiles = new TreeSet<>();
258+
List<String> activeProfiles = new ArrayList<>();
259+
213260
for (PropertySource<?> propertySource : environment.getPropertySources()) {
214-
addIncludedProfilesTo(includeProfiles, propertySource);
261+
addIncludedProfilesTo(includeProfiles, propertySource, environment);
262+
addActiveProfilesTo(activeProfiles, propertySource, environment);
215263
}
216-
List<String> activeProfiles = new ArrayList<>();
217-
Collections.addAll(activeProfiles, environment.getActiveProfiles());
218264

219265
// If it's already accepted we assume the order was set intentionally
220266
includeProfiles.removeAll(activeProfiles);
221-
if (includeProfiles.isEmpty()) {
222-
return;
223-
}
224267
// Prepend each added profile (last wins in a property key clash)
225268
for (String profile : includeProfiles) {
226269
activeProfiles.add(0, profile);
227270
}
271+
List<String> activeProfilesFromEnvironment = Arrays.stream(environment.getActiveProfiles())
272+
.collect(Collectors.toList());
273+
if (!activeProfiles.containsAll(activeProfilesFromEnvironment)) {
274+
activeProfiles.addAll(activeProfilesFromEnvironment);
275+
276+
}
228277
environment.setActiveProfiles(activeProfiles.toArray(new String[activeProfiles.size()]));
229278
}
230279

231-
private Set<String> addIncludedProfilesTo(Set<String> profiles, PropertySource<?> propertySource) {
280+
private Set<String> addIncludedProfilesTo(Set<String> profiles, PropertySource<?> propertySource,
281+
ConfigurableEnvironment environment) {
282+
return addProfilesTo(profiles, propertySource, Profiles.INCLUDE_PROFILES_PROPERTY_NAME, environment);
283+
}
284+
285+
private List<String> addActiveProfilesTo(List<String> profiles, PropertySource<?> propertySource,
286+
ConfigurableEnvironment environment) {
287+
return addProfilesTo(profiles, propertySource, AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, environment);
288+
}
289+
290+
private <T extends Collection<String>> T addProfilesTo(T profiles, PropertySource<?> propertySource,
291+
String property, ConfigurableEnvironment environment) {
232292
if (propertySource instanceof CompositePropertySource) {
233293
for (PropertySource<?> nestedPropertySource : ((CompositePropertySource) propertySource)
234294
.getPropertySources()) {
235-
addIncludedProfilesTo(profiles, nestedPropertySource);
295+
addProfilesTo(profiles, nestedPropertySource, property, environment);
236296
}
237297
}
238298
else {
239299
Collections.addAll(profiles, getProfilesForValue(
240-
propertySource.getProperty(BootstrapConfigFileApplicationListener.INCLUDE_PROFILES_PROPERTY)));
300+
propertySource.getProperty(BootstrapConfigFileApplicationListener.INCLUDE_PROFILES_PROPERTY),
301+
environment));
241302
}
242303
return profiles;
243304
}
244305

245-
private String[] getProfilesForValue(Object property) {
306+
private String[] getProfilesForValue(Object property, ConfigurableEnvironment environment) {
246307
final String value = (property == null ? null : property.toString());
247-
return property == null ? new String[0] : StringUtils.tokenizeToStringArray(value, ",");
308+
return property == null ? new String[0] : resolvePlaceholdersInProfiles(value, environment);
309+
}
310+
311+
private String[] resolvePlaceholdersInProfiles(String profiles, ConfigurableEnvironment environment) {
312+
return Arrays.stream(StringUtils.tokenizeToStringArray(profiles, ",")).map(s -> {
313+
if (s.startsWith("${") && s.endsWith("}")) {
314+
return environment.resolvePlaceholders(s);
315+
}
316+
else {
317+
return s;
318+
}
319+
}).toArray(String[]::new);
320+
}
321+
322+
/*
323+
* The ConextRefreshedEvent gets called at the end of the boostrap phase after config
324+
* data is loaded during bootstrap. This will run and do an "initial fetch" of
325+
* configuration data during bootstrap but before the main applicaiton context starts.
326+
*/
327+
@Override
328+
public void onApplicationEvent(ContextRefreshedEvent event) {
329+
if (bootstrapProperties.isInitializeOnContextRefresh()
330+
&& event.getApplicationContext() instanceof ConfigurableApplicationContext) {
331+
if (((ConfigurableApplicationContext) event.getApplicationContext()).getEnvironment().getPropertySources()
332+
.contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
333+
doInitialize((ConfigurableApplicationContext) event.getApplicationContext());
334+
}
335+
}
248336
}
249337

250338
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ public class PropertySourceBootstrapProperties {
4646
*/
4747
private boolean overrideNone = false;
4848

49+
/**
50+
* Flag to initialize bootstrap configuration on context refresh event. Default false.
51+
*/
52+
private boolean initializeOnContextRefresh = false;
53+
54+
public boolean isInitializeOnContextRefresh() {
55+
return initializeOnContextRefresh;
56+
}
57+
58+
public void setInitializeOnContextRefresh(boolean initializeOnContextRefresh) {
59+
this.initializeOnContextRefresh = initializeOnContextRefresh;
60+
}
61+
4962
public boolean isOverrideNone() {
5063
return this.overrideNone;
5164
}

0 commit comments

Comments
 (0)