Skip to content

Commit c1be5cb

Browse files
committed
Add limited support for spring.profiles.include
Restore support for the `spring.profiles.include` property but only for non-profile specific documents. Closes gh-22944
1 parent 6cfd2e3 commit c1be5cb

8 files changed

+90
-27
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java

+36-1
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,20 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.LinkedHashSet;
2123
import java.util.List;
24+
import java.util.Set;
2225

2326
import org.apache.commons.logging.Log;
2427

2528
import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption;
2629
import org.springframework.boot.context.properties.bind.BindException;
30+
import org.springframework.boot.context.properties.bind.Bindable;
2731
import org.springframework.boot.context.properties.bind.Binder;
32+
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
33+
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
34+
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
2835
import org.springframework.boot.env.BootstrapRegistry;
2936
import org.springframework.boot.env.DefaultPropertiesPropertySource;
3037
import org.springframework.boot.logging.DeferredLogFactory;
@@ -74,6 +81,11 @@ class ConfigDataEnvironment {
7481

7582
private static final String[] EMPTY_LOCATIONS = new String[0];
7683

84+
private static final ConfigurationPropertyName INCLUDE_PROFILES = ConfigurationPropertyName
85+
.of(Profiles.INCLUDE_PROFILES_PROPERTY_NAME);
86+
87+
private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);
88+
7789
private final DeferredLogFactory logFactory;
7890

7991
private final Log logger;
@@ -212,7 +224,9 @@ private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributo
212224
this.logger.trace("Deducing profiles from current config data environment contributors");
213225
Binder binder = contributors.getBinder(activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
214226
try {
215-
Profiles profiles = new Profiles(this.environment, binder, this.additionalProfiles);
227+
Set<String> additionalProfiles = new LinkedHashSet<>(this.additionalProfiles);
228+
additionalProfiles.addAll(getIncludedProfiles(contributors, activationContext));
229+
Profiles profiles = new Profiles(this.environment, binder, additionalProfiles);
216230
return activationContext.withProfiles(profiles);
217231
}
218232
catch (BindException ex) {
@@ -223,6 +237,27 @@ private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributo
223237
}
224238
}
225239

240+
private Collection<? extends String> getIncludedProfiles(ConfigDataEnvironmentContributors contributors,
241+
ConfigDataActivationContext activationContext) {
242+
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
243+
contributors, activationContext, true);
244+
Set<String> result = new LinkedHashSet<>();
245+
for (ConfigDataEnvironmentContributor contributor : contributors) {
246+
ConfigurationPropertySource source = contributor.getConfigurationPropertySource();
247+
if (source == null) {
248+
continue;
249+
}
250+
Binder binder = new Binder(Collections.singleton(source), placeholdersResolver);
251+
binder.bind(INCLUDE_PROFILES, STRING_LIST).ifBound((includes) -> {
252+
if (!contributor.isActive(activationContext)) {
253+
InactiveConfigDataAccessException.throwIfPropertyFound(contributor, INCLUDE_PROFILES);
254+
}
255+
result.addAll(includes);
256+
});
257+
}
258+
return result;
259+
}
260+
226261
private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,
227262
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
228263
this.logger.trace("Processing config data environment contributors with profile activation context");

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java

+4-14
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,12 @@
3535
*/
3636
public class InvalidConfigDataPropertyException extends ConfigDataException {
3737

38-
private static final Map<ConfigurationPropertyName, ConfigurationPropertyName> ERROR;
39-
4038
private static final Map<ConfigurationPropertyName, ConfigurationPropertyName> WARNING;
4139
static {
4240
Map<ConfigurationPropertyName, ConfigurationPropertyName> warning = new LinkedHashMap<>();
4341
warning.put(ConfigurationPropertyName.of("spring.profiles"),
4442
ConfigurationPropertyName.of("spring.config.activate.on-profile"));
4543
WARNING = Collections.unmodifiableMap(warning);
46-
Map<ConfigurationPropertyName, ConfigurationPropertyName> error = new LinkedHashMap<>();
47-
error.put(ConfigurationPropertyName.of("spring.profiles.include"),
48-
ConfigurationPropertyName.of("spring.profiles.group"));
49-
ERROR = Collections.unmodifiableMap(error);
5044
}
5145

5246
private final ConfigurationProperty property;
@@ -90,20 +84,16 @@ public ConfigurationPropertyName getReplacement() {
9084
}
9185

9286
/**
93-
* Throw a {@link InvalidConfigDataPropertyException} if the given
94-
* {@link ConfigDataEnvironmentContributor} contains any invalid property.
87+
* Throw a {@link InvalidConfigDataPropertyException} or log a warning if the given
88+
* {@link ConfigDataEnvironmentContributor} contains any invalid property. A warning
89+
* is logged if the property is still supported, but not recommended. An error is
90+
* thrown if the property is completely unsupported.
9591
* @param logger the logger to use for warnings
9692
* @param contributor the contributor to check
9793
*/
9894
static void throwOrWarn(Log logger, ConfigDataEnvironmentContributor contributor) {
9995
ConfigurationPropertySource propertySource = contributor.getConfigurationPropertySource();
10096
if (propertySource != null) {
101-
ERROR.forEach((invalid, replacement) -> {
102-
ConfigurationProperty property = propertySource.getConfigurationProperty(invalid);
103-
if (property != null) {
104-
throw new InvalidConfigDataPropertyException(property, replacement, contributor.getLocation());
105-
}
106-
});
10797
WARNING.forEach((invalid, replacement) -> {
10898
ConfigurationProperty property = propertySource.getConfigurationProperty(invalid);
10999
if (property != null) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/Profiles.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
*/
5050
public class Profiles implements Iterable<String> {
5151

52+
/**
53+
* Name of property to set to specify additionally included active profiles.
54+
*/
55+
public static final String INCLUDE_PROFILES_PROPERTY_NAME = "spring.profiles.include";
56+
5257
private static final Bindable<MultiValueMap<String, String>> STRING_STRINGS_MAP = Bindable
5358
.of(ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class));
5459

@@ -67,7 +72,7 @@ public class Profiles implements Iterable<String> {
6772
* {@link Binder}.
6873
* @param environment the source environment
6974
* @param binder the binder for profile properties
70-
* @param additionalProfiles and additional active profiles
75+
* @param additionalProfiles any additional active profiles
7176
*/
7277
Profiles(Environment environment, Binder binder, Collection<String> additionalProfiles) {
7378
this.groups = binder.bind("spring.profiles.group", STRING_STRINGS_MAP).orElseGet(LinkedMultiValueMap::new);

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java

+22
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,28 @@ void runWhenHasNonOptionalImportThrowsException() {
549549
() -> this.application.run("--spring.config.location=classpath:missing-appplication.properties"));
550550
}
551551

552+
@Test
553+
void runWhenHasIncludedProfilesActivatesProfiles() {
554+
ConfigurableApplicationContext context = this.application
555+
.run("--spring.config.location=classpath:application-include-profiles.properties");
556+
assertThat(context.getEnvironment().getActiveProfiles()).containsExactlyInAnyOrder("p1", "p2", "p3", "p4",
557+
"p5");
558+
}
559+
560+
@Test
561+
void runWhenHasIncludedProfilesWithPlaceholderActivatesProfiles() {
562+
ConfigurableApplicationContext context = this.application
563+
.run("--spring.config.location=classpath:application-include-profiles-with-placeholder.properties");
564+
assertThat(context.getEnvironment().getActiveProfiles()).containsExactlyInAnyOrder("p1", "p2", "p3", "p4",
565+
"p5");
566+
}
567+
568+
@Test
569+
void runWhenHasIncludedProfilesWithProfileSpecificDocumentThrowsException() {
570+
assertThatExceptionOfType(InactiveConfigDataAccessException.class).isThrownBy(() -> this.application
571+
.run("--spring.config.location=classpath:application-include-profiles-in-profile-specific.properties"));
572+
}
573+
552574
private Condition<ConfigurableEnvironment> matchingPropertySource(final String sourceName) {
553575
return new Condition<ConfigurableEnvironment>("environment containing property source " + sourceName) {
554576

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InvalidConfigDataPropertyExceptionTests.java

-11
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,6 @@ void throwOrWarnWhenHasWarningPropertyLogsWarning() {
123123
+ "'spring.config.activate.on-profile' [origin: \"spring.profiles\" from property source \"mockProperties\"]");
124124
}
125125

126-
@Test
127-
void throwOrWarnWhenHasErrorPropertyThrowsException() {
128-
MockPropertySource propertySource = new MockPropertySource();
129-
propertySource.setProperty("spring.profiles.include", "a");
130-
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
131-
assertThatExceptionOfType(InvalidConfigDataPropertyException.class)
132-
.isThrownBy(() -> InvalidConfigDataPropertyException.throwOrWarn(this.logger, contributor))
133-
.withMessage("Property 'spring.profiles.include' is invalid and should be replaced with "
134-
+ "'spring.profiles.group' [origin: \"spring.profiles.include\" from property source \"mockProperties\"]");
135-
}
136-
137126
private static class TestConfigDataLocation extends ConfigDataLocation {
138127

139128
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
spring.profiles.active=p1
2+
spring.profiles.include=p2
3+
#---
4+
spring.config.activate.on-profile=p2
5+
spring.profiles.include=p3
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
spring.profiles.active=p1
2+
spring.profiles.include=p2
3+
#---
4+
myprofile=p4
5+
spring.profiles.include=p3,${myprofile}
6+
#---
7+
myotherprofile=p5
8+
spring.profiles.include=${myotherprofile}
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spring.profiles.active=p1
2+
spring.profiles.include=p2
3+
#---
4+
spring.profiles.include=p3,p4
5+
#---
6+
spring.profiles.include=p5
7+

0 commit comments

Comments
 (0)