diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java index b902fd87da8d..5a1431d21aa4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,7 +90,13 @@ public enum Option { /** * Ignore all imports properties from the sources. */ - IGNORE_IMPORTS; + IGNORE_IMPORTS, + + /** + * Ignore all profile activation and include properties. + * @since 2.4.3 + */ + IGNORE_PROFILES; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java index a01c9be06578..3ea39a829fab 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java @@ -181,6 +181,11 @@ private ConfigDataEnvironmentContributors createContributors(Binder binder) { this.logger.trace("Creating wrapped config data contributor for default property source"); contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource)); } + return createContributors(contributors); + } + + protected ConfigDataEnvironmentContributors createContributors( + List contributors) { return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors); } @@ -263,7 +268,8 @@ private ConfigDataEnvironmentContributors processWithoutProfiles(ConfigDataEnvir private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributors contributors, ConfigDataActivationContext activationContext) { this.logger.trace("Deducing profiles from current config data environment contributors"); - Binder binder = contributors.getBinder(activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); + Binder binder = contributors.getBinder(activationContext, + ConfigDataEnvironmentContributor::isNotIgnoringProfiles, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); try { Set additionalProfiles = new LinkedHashSet<>(this.additionalProfiles); additionalProfiles.addAll(getIncludedProfiles(contributors, activationContext)); @@ -285,16 +291,15 @@ private Collection getIncludedProfiles(ConfigDataEnvironmentCo Set result = new LinkedHashSet<>(); for (ConfigDataEnvironmentContributor contributor : contributors) { ConfigurationPropertySource source = contributor.getConfigurationPropertySource(); - if (source == null) { - continue; + if (source != null && contributor.isNotIgnoringProfiles()) { + Binder binder = new Binder(Collections.singleton(source), placeholdersResolver); + binder.bind(Profiles.INCLUDE_PROFILES, STRING_LIST).ifBound((includes) -> { + if (!contributor.isActive(activationContext)) { + InactiveConfigDataAccessException.throwIfPropertyFound(contributor, Profiles.INCLUDE_PROFILES); + } + result.addAll(includes); + }); } - Binder binder = new Binder(Collections.singleton(source), placeholdersResolver); - binder.bind(Profiles.INCLUDE_PROFILES, STRING_LIST).ifBound((includes) -> { - if (!contributor.isActive(activationContext)) { - InactiveConfigDataAccessException.throwIfPropertyFound(contributor, Profiles.INCLUDE_PROFILES); - } - result.addAll(includes); - }); } return result; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java index d4f6bd463784..c53265dec448 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -51,6 +52,9 @@ */ class ConfigDataEnvironmentContributor implements Iterable { + private static final Set EMPTY_LOCATION_OPTIONS = Collections + .unmodifiableSet(Collections.singleton(ConfigData.Option.IGNORE_IMPORTS)); + private final ConfigDataLocation location; private final ConfigDataResource resource; @@ -63,7 +67,7 @@ class ConfigDataEnvironmentContributor implements Iterable configDataOptions; private final Map> children; @@ -79,13 +83,14 @@ class ConfigDataEnvironmentContributor implements Iterable propertySource, ConfigurationPropertySource configurationPropertySource, ConfigDataProperties properties, - boolean ignoreImports, Map> children) { + Set configDataOptions, + Map> children) { this.kind = kind; this.location = location; this.resource = resource; @@ -93,7 +98,7 @@ class ConfigDataEnvironmentContributor implements Iterable iterator() { ConfigDataEnvironmentContributor withBoundProperties(Binder binder) { UseLegacyConfigProcessingException.throwIfRequested(binder); ConfigDataProperties properties = ConfigDataProperties.get(binder); - if (this.ignoreImports) { + if (this.configDataOptions.contains(ConfigData.Option.IGNORE_IMPORTS)) { properties = properties.withoutImports(); } return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, this.location, this.resource, this.profileSpecific, this.propertySource, this.configurationPropertySource, properties, - this.ignoreImports, null); + this.configDataOptions, null); } /** @@ -229,7 +243,7 @@ ConfigDataEnvironmentContributor withChildren(ImportPhase importPhase, Map> updatedChildren = new LinkedHashMap<>(this.children); updatedChildren.put(importPhase, children); return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource, this.profileSpecific, - this.propertySource, this.configurationPropertySource, this.properties, this.ignoreImports, + this.propertySource, this.configurationPropertySource, this.properties, this.configDataOptions, updatedChildren); } @@ -255,7 +269,7 @@ ConfigDataEnvironmentContributor withReplacement(ConfigDataEnvironmentContributo updatedChildren.put(importPhase, Collections.unmodifiableList(updatedContributors)); }); return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource, this.profileSpecific, - this.propertySource, this.configurationPropertySource, this.properties, this.ignoreImports, + this.propertySource, this.configurationPropertySource, this.properties, this.configDataOptions, updatedChildren); } @@ -267,7 +281,7 @@ ConfigDataEnvironmentContributor withReplacement(ConfigDataEnvironmentContributo static ConfigDataEnvironmentContributor of(List contributors) { Map> children = new LinkedHashMap<>(); children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors)); - return new ConfigDataEnvironmentContributor(Kind.ROOT, null, null, false, null, null, null, false, children); + return new ConfigDataEnvironmentContributor(Kind.ROOT, null, null, false, null, null, null, null, children); } /** @@ -281,7 +295,7 @@ static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initi List imports = Collections.singletonList(initialImport); ConfigDataProperties properties = new ConfigDataProperties(imports, null); return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT, null, null, false, null, null, properties, - false, null); + null, null); } /** @@ -293,7 +307,7 @@ static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initi */ static ConfigDataEnvironmentContributor ofExisting(PropertySource propertySource) { return new ConfigDataEnvironmentContributor(Kind.EXISTING, null, null, false, propertySource, - ConfigurationPropertySource.from(propertySource), null, false, null); + ConfigurationPropertySource.from(propertySource), null, null, null); } /** @@ -311,9 +325,8 @@ static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataLocation locat boolean profileSpecific, ConfigData configData, int propertySourceIndex) { PropertySource propertySource = configData.getPropertySources().get(propertySourceIndex); ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource); - boolean ignoreImports = configData.getOptions().contains(ConfigData.Option.IGNORE_IMPORTS); return new ConfigDataEnvironmentContributor(Kind.UNBOUND_IMPORT, location, resource, profileSpecific, - propertySource, configurationPropertySource, null, ignoreImports, null); + propertySource, configurationPropertySource, null, configData.getOptions(), null); } /** @@ -324,7 +337,7 @@ static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataLocation locat */ static ConfigDataEnvironmentContributor ofEmptyLocation(ConfigDataLocation location, boolean profileSpecific) { return new ConfigDataEnvironmentContributor(Kind.EMPTY_LOCATION, location, null, profileSpecific, null, null, - null, true, null); + null, EMPTY_LOCATION_OPTIONS, null); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java index a3c345d70fed..e40a4e46f431 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java @@ -24,8 +24,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.logging.Log; @@ -52,6 +52,8 @@ */ class ConfigDataEnvironmentContributors implements Iterable { + private static final Predicate NO_CONTRIBUTOR_FILTER = (contributor) -> true; + private final Log logger; private final ConfigDataEnvironmentContributor root; @@ -186,13 +188,25 @@ ConfigDataEnvironmentContributor getRoot() { } /** - * Return a {@link Binder} that works against all active contributors. + * Return a {@link Binder} backed by the contributors. * @param activationContext the activation context * @param options binder options to apply * @return a binder instance */ Binder getBinder(ConfigDataActivationContext activationContext, BinderOption... options) { - return getBinder(activationContext, asBinderOptionsSet(options)); + return getBinder(activationContext, NO_CONTRIBUTOR_FILTER, options); + } + + /** + * Return a {@link Binder} backed by the contributors. + * @param activationContext the activation context + * @param filter a filter used to limit the contributors + * @param options binder options to apply + * @return a binder instance + */ + Binder getBinder(ConfigDataActivationContext activationContext, Predicate filter, + BinderOption... options) { + return getBinder(activationContext, filter, asBinderOptionsSet(options)); } private Set asBinderOptionsSet(BinderOption... options) { @@ -200,10 +214,11 @@ private Set asBinderOptionsSet(BinderOption... options) { : EnumSet.copyOf(Arrays.asList(options)); } - private Binder getBinder(ConfigDataActivationContext activationContext, Set options) { + private Binder getBinder(ConfigDataActivationContext activationContext, + Predicate filter, Set options) { boolean failOnInactiveSource = options.contains(BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); Iterable sources = () -> getBinderSources(activationContext, - !options.contains(BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE)); + filter.and((contributor) -> failOnInactiveSource || contributor.isActive(activationContext))); PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(this.root, activationContext, failOnInactiveSource); BindHandler bindHandler = !failOnInactiveSource ? null : new InactiveSourceChecker(activationContext); @@ -211,13 +226,9 @@ private Binder getBinder(ConfigDataActivationContext activationContext, Set getBinderSources(ConfigDataActivationContext activationContext, - boolean filterInactive) { - Stream sources = this.root.stream() - .filter(this::hasConfigurationPropertySource); - if (filterInactive) { - sources = sources.filter((contributor) -> contributor.isActive(activationContext)); - } - return sources.map(ConfigDataEnvironmentContributor::getConfigurationPropertySource).iterator(); + Predicate filter) { + return this.root.stream().filter(this::hasConfigurationPropertySource).filter(filter) + .map(ConfigDataEnvironmentContributor::getConfigurationPropertySource).iterator(); } private boolean hasConfigurationPropertySource(ConfigDataEnvironmentContributor contributor) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java index cc85e435f315..21be4abaac37 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java @@ -114,7 +114,7 @@ static void throwOrWarn(Log logger, ConfigDataEnvironmentContributor contributor logger.warn(getMessage(property, false, replacement, contributor.getResource())); } }); - if (contributor.isProfileSpecific()) { + if (contributor.isProfileSpecific() && contributor.isNotIgnoringProfiles()) { PROFILE_SPECIFIC_ERRORS.forEach((name) -> { ConfigurationProperty property = propertySource.getConfigurationProperty(name); if (property != null) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolverTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolverTests.java index 47e96695fbaa..6186352c3b7a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolverTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolverTests.java @@ -122,7 +122,7 @@ static class TestConfigDataEnvironmentContributor extends ConfigDataEnvironmentC private final boolean active; protected TestConfigDataEnvironmentContributor(PropertySource propertySource, boolean active) { - super(Kind.ROOT, null, null, false, propertySource, null, null, false, null); + super(Kind.ROOT, null, null, false, propertySource, null, null, null, null); this.active = active; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java index 3d0d07e5cb96..c690f079f51c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -34,6 +36,7 @@ import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; @@ -42,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.mock; /** * Tests for {@link ConfigDataEnvironment}. @@ -188,6 +192,30 @@ void processAndApplySetsActiveProfilesAndProfileGroups(TestInfo info) { assertThat(this.environment.getActiveProfiles()).containsExactly("one", "four", "five", "two", "three"); } + @Test + void processAndApplyDoesNotSetProfilesFromIgnoreProfilesContributors(TestInfo info) { + this.environment.setProperty("spring.config.location", getConfigLocation(info)); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, + this.environment, this.resourceLoader, this.additionalProfiles, null) { + + @Override + protected ConfigDataEnvironmentContributors createContributors( + List contributors) { + Map source = new LinkedHashMap<>(); + source.put("spring.profiles.active", "ignore1"); + source.put("spring.profiles.include", "ignore2"); + ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)), + ConfigData.Option.IGNORE_PROFILES); + contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"), + mock(ConfigDataResource.class), false, data, 0)); + return super.createContributors(contributors); + } + + }; + configDataEnvironment.processAndApply(); + assertThat(this.environment.getActiveProfiles()).containsExactly("test"); + } + @Test @Disabled("Disabled until spring.profiles support is dropped") void processAndApplyWhenHasInvalidPropertyThrowsException() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InvalidConfigDataPropertyExceptionTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InvalidConfigDataPropertyExceptionTests.java index bd94cb365f1a..fae33cb88003 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InvalidConfigDataPropertyExceptionTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InvalidConfigDataPropertyExceptionTests.java @@ -16,6 +16,9 @@ package org.springframework.boot.context.config; +import java.util.Arrays; +import java.util.HashSet; + import org.apache.commons.logging.Log; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -29,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -125,16 +129,31 @@ void throwOrWarnWhenWhenHasInvalidProfileSpecificPropertyThrowsException() { throwOrWarnWhenWhenHasInvalidProfileSpecificPropertyThrowsException("spring.profiles"); } + @Test + void throwOrWarnWhenWhenHasInvalidProfileSpecificPropertyOnIgnoringProfilesContributorDoesNotThrowException() { + ConfigDataEnvironmentContributor contributor = createInvalidProfileSpecificPropertyContributor( + "spring.profiles.active", ConfigData.Option.IGNORE_PROFILES); + assertThatNoException() + .isThrownBy(() -> InvalidConfigDataPropertyException.throwOrWarn(this.logger, contributor)); + } + private void throwOrWarnWhenWhenHasInvalidProfileSpecificPropertyThrowsException(String name) { - MockPropertySource propertySource = new MockPropertySource(); - propertySource.setProperty(name, "a"); - ConfigDataEnvironmentContributor contributor = new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, null, - null, true, propertySource, ConfigurationPropertySource.from(propertySource), null, false, null); + ConfigDataEnvironmentContributor contributor = createInvalidProfileSpecificPropertyContributor(name); assertThatExceptionOfType(InvalidConfigDataPropertyException.class) .isThrownBy(() -> InvalidConfigDataPropertyException.throwOrWarn(this.logger, contributor)) .withMessageStartingWith("Property '" + name + "' is invalid in a profile specific resource"); } + private ConfigDataEnvironmentContributor createInvalidProfileSpecificPropertyContributor(String name, + ConfigData.Option... configDataOptions) { + MockPropertySource propertySource = new MockPropertySource(); + propertySource.setProperty(name, "a"); + ConfigDataEnvironmentContributor contributor = new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, null, + null, true, propertySource, ConfigurationPropertySource.from(propertySource), null, + new HashSet<>(Arrays.asList(configDataOptions)), null); + return contributor; + } + @Test void throwOrWarnWhenHasNoInvalidPropertyDoesNothing() { ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor diff --git a/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplyDoesNotSetProfilesFromIgnoreProfilesContributors.properties b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplyDoesNotSetProfilesFromIgnoreProfilesContributors.properties new file mode 100644 index 000000000000..f841722a2f15 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplyDoesNotSetProfilesFromIgnoreProfilesContributors.properties @@ -0,0 +1 @@ +spring.profiles.active=test