From 3352024b1ca641fa0beaaa4cd87788c190242eb0 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 13 Jul 2020 21:19:10 -0700 Subject: [PATCH] Provide ConfigFileApplicationListener replacement Deprecate `ConfigFileApplicationListener` and provide a replacement mechanism that supports arbitrary config data imports. This commit updates the following areas: - Extract `EnvironmentPostProcessor` invocation logic from the `ConfigFileApplicationListener` to new dedicated listener. Also providing support for `Log` injection. - Extract `RandomPropertySource` adding logic from the `ConfigFileApplicationListener` to a dedicated class. - Migrate to the recently introduced `DefaultPropertiesPropertySource` class when moving the defaultProperties `PropertySource` - Replace processing logic with a phased approach to ensure that profile enablement happens in a distinct phase and that profiles can no longer be activated on an ad-hoc basis. - Provide a more predictable and logical import order for processing `application.properties` and `application.yml` files. - Add support for a `spring.config.import` property which can be used to import additional config data. Also provide a pluggable API allowing third-parties to resolve and load locations themselves. - Add `spring.config.activate.on-profile` support which replaces the existing `spring.profiles` property. - Add `spring.config.activate.on-cloud-platform` support which allows a config data document to be active only on a given cloud platform. - Support a `spring.config.use-legacy-processing` property allowing the previous processing logic to be used. Closes gh-22497 Co-authored-by: Madhura Bhave --- .../devtools/RemoteSpringApplication.java | 7 +- ...nfigDataApplicationContextInitializer.java | 63 ++ ...nfigFileApplicationContextInitializer.java | 9 +- ...ataApplicationContextInitializerTests.java | 55 ++ ...ntextInitializerWithLegacySwitchTests.java | 57 ++ ...ileApplicationContextInitializerTests.java | 4 +- .../boot/SpringApplication.java | 27 +- ...udFoundryVcapEnvironmentPostProcessor.java | 46 +- .../config/AnsiOutputApplicationListener.java | 7 +- .../boot/context/config/ConfigData.java | 92 +++ .../config/ConfigDataActivationContext.java | 100 ++++ .../context/config/ConfigDataEnvironment.java | 260 +++++++++ .../ConfigDataEnvironmentContributor.java | 395 +++++++++++++ ...onmentContributorPlaceholdersResolver.java | 78 +++ .../ConfigDataEnvironmentContributors.java | 251 ++++++++ .../ConfigDataEnvironmentPostProcessor.java | 110 ++++ .../context/config/ConfigDataException.java | 37 ++ .../context/config/ConfigDataImporter.java | 83 +++ .../boot/context/config/ConfigDataLoader.java | 58 ++ .../context/config/ConfigDataLoaders.java | 112 ++++ .../context/config/ConfigDataLocation.java | 30 + .../config/ConfigDataLocationResolver.java | 84 +++ .../ConfigDataLocationResolverContext.java | 44 ++ .../config/ConfigDataLocationResolvers.java | 150 +++++ .../context/config/ConfigDataProperties.java | 186 ++++++ .../config/ConfigFileApplicationListener.java | 92 ++- .../config/FilteredPropertySource.java | 4 +- .../InactiveConfigDataAccessException.java | 130 +++++ .../boot/context/config/Instantiator.java | 162 ++++++ .../InvalidConfigDataPropertyException.java | 134 +++++ .../boot/context/config/Profiles.java | 173 ++++++ .../config/ResourceConfigDataLoader.java | 36 ++ .../config/ResourceConfigDataLocation.java | 95 +++ .../ResourceConfigDataLocationResolver.java | 360 ++++++++++++ ...nsupportedConfigDataLocationException.java | 47 ++ .../UseLegacyConfigProcessingException.java | 94 +++ .../boot/context/config/package-info.java | 4 +- .../boot/env/EnvironmentPostProcessor.java | 17 +- ...nmentPostProcessorApplicationListener.java | 181 ++++++ .../boot/env/RandomValuePropertySource.java | 25 +- ...ropertySourceEnvironmentPostProcessor.java | 58 ++ ...itional-spring-configuration-metadata.json | 43 +- .../main/resources/META-INF/spring.factories | 13 +- .../boot/SpringApplicationTests.java | 4 +- ...ndryVcapEnvironmentPostProcessorTests.java | 4 +- .../ConfigDataActivationContextTests.java | 99 ++++ ...tContributorPlaceholdersResolverTests.java | 136 +++++ ...ConfigDataEnvironmentContributorTests.java | 359 ++++++++++++ ...onfigDataEnvironmentContributorsTests.java | 349 +++++++++++ ...ironmentPostProcessorIntegrationTests.java | 540 ++++++++++++++++++ ...nfigDataEnvironmentPostProcessorTests.java | 121 ++++ .../config/ConfigDataEnvironmentTests.java | 214 +++++++ .../config/ConfigDataImporterTests.java | 117 ++++ .../context/config/ConfigDataLoaderTests.java | 53 ++ .../config/ConfigDataLoadersTests.java | 166 ++++++ .../ConfigDataLocationResolverTests.java | 57 ++ .../ConfigDataLocationResolversTests.java | 222 +++++++ .../config/ConfigDataPropertiesTests.java | 200 +++++++ .../boot/context/config/ConfigDataTests.java | 69 +++ ...eApplicationListenerLegacyReproTests.java} | 11 +- .../ConfigFileApplicationListenerTests.java | 78 +-- ...ationListenerYamlProfileNegationTests.java | 4 +- .../config/FilteredPropertySourceTests.java | 3 +- ...nactiveConfigDataAccessExceptionTests.java | 141 +++++ .../context/config/InstantiatorTests.java | 161 ++++++ ...validConfigDataPropertyExceptionTests.java | 135 +++++ .../boot/context/config/ProfilesTests.java | 275 +++++++++ .../config/ResourceConfigDataLoaderTests.java | 63 ++ ...sourceConfigDataLocationResolverTests.java | 230 ++++++++ .../ResourceConfigDataLocationTests.java | 85 +++ ...ortedConfigDataLocationExceptionTests.java | 43 ++ ...eLegacyConfigProcessingExceptionTests.java | 61 ++ .../context/config/UseLegacyProcessing.java | 51 ++ ...PostProcessorApplicationListenerTests.java | 33 ++ ...tySourceEnvironmentPostProcessorTests.java | 51 ++ .../env/RandomValuePropertySourceTests.java | 54 +- .../profiles/testnegatedprofiles.yml | 17 + .../profiles/testprofileexpression.yml | 12 + .../configdata/profiles/testprofiles.yml | 15 + .../profiles/testprofilesdocument.yml | 12 + .../configdata/profiles/testprofilesempty.yml | 12 + .../configdata/profiles/testsetprofiles.yml | 10 + .../properties/application-dev.properties | 0 .../configdata/properties/application.other | 0 .../properties/application.properties | 1 + .../configdata/properties/other.properties | 0 .../resources/configdata/yaml/application.yml | 3 + .../src/test/resources/customapplication.yml | 1 + .../test/resources/invalidproperty.properties | 1 + ...AddsImportedSourceToEnvironment.properties | 1 + ...MovesDefaultProperySourceToLast.properties | 1 + ...ApplyOnlyAddsActiveContributors.properties | 4 + ...ocessAndApplySetsActiveProfiles.properties | 1 + ...cessAndApplySetsDefaultProfiles.properties | 1 + .../resources/testprofiles-default.properties | 1 + .../src/main/resources/application.yml | 3 +- 96 files changed, 8130 insertions(+), 168 deletions(-) create mode 100644 spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerTests.java create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerWithLegacySwitchTests.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataActivationContext.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessor.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataException.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataImporter.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocation.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataProperties.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InactiveConfigDataAccessException.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/Instantiator.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/Profiles.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocation.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocationResolver.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/UnsupportedConfigDataLocationException.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/UseLegacyConfigProcessingException.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessor.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataActivationContextTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolverTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolverTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolversTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataPropertiesTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataTests.java rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/{ReproTests.java => context/config/ConfigFileApplicationListenerLegacyReproTests.java} (94%) create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InactiveConfigDataAccessExceptionTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InstantiatorTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/InvalidConfigDataPropertyExceptionTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ProfilesTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLoaderTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLocationResolverTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLocationTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/UnsupportedConfigDataLocationExceptionTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/UseLegacyConfigProcessingExceptionTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/UseLegacyProcessing.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/RandomValuePropertySourceEnvironmentPostProcessorTests.java create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/profiles/testnegatedprofiles.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/profiles/testprofileexpression.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/profiles/testprofiles.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/profiles/testprofilesdocument.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/profiles/testprofilesempty.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/profiles/testsetprofiles.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/properties/application-dev.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/properties/application.other create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/properties/application.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/properties/other.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/configdata/yaml/application.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/customapplication.yml create mode 100644 spring-boot-project/spring-boot/src/test/resources/invalidproperty.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplyAddsImportedSourceToEnvironment.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplyMovesDefaultProperySourceToLast.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplyOnlyAddsActiveContributors.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplySetsActiveProfiles.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/context/config/ConfigDataEnvironmentTests-processAndApplySetsDefaultProfiles.properties create mode 100644 spring-boot-project/spring-boot/src/test/resources/testprofiles-default.properties diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java index 2e2b17349f53..354e1de2c42e 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/RemoteSpringApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -25,13 +25,14 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.context.config.AnsiOutputApplicationListener; -import org.springframework.boot.context.config.ConfigFileApplicationListener; +import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; import org.springframework.boot.context.logging.ClasspathLoggingApplicationListener; import org.springframework.boot.context.logging.LoggingApplicationListener; import org.springframework.boot.devtools.remote.client.RemoteClientConfiguration; import org.springframework.boot.devtools.restart.RestartInitializer; import org.springframework.boot.devtools.restart.RestartScopeInitializer; import org.springframework.boot.devtools.restart.Restarter; +import org.springframework.boot.env.EnvironmentPostProcessorApplicationListener; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.core.io.ClassPathResource; @@ -71,7 +72,7 @@ private Collection> getInitializers() { private Collection> getListeners() { List> listeners = new ArrayList<>(); listeners.add(new AnsiOutputApplicationListener()); - listeners.add(new ConfigFileApplicationListener()); + listeners.add(new EnvironmentPostProcessorApplicationListener(ConfigDataEnvironmentPostProcessor.class)); listeners.add(new ClasspathLoggingApplicationListener()); listeners.add(new LoggingApplicationListener()); listeners.add(new RemoteUrlPropertyExtractor()); diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java new file mode 100644 index 000000000000..40261ffe9628 --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.context; + +import java.util.function.Supplier; + +import org.springframework.boot.context.config.ConfigData; +import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; +import org.springframework.boot.env.DefaultPropertiesPropertySource; +import org.springframework.boot.env.RandomValuePropertySource; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.test.context.ContextConfiguration; + +/** + * {@link ApplicationContextInitializer} that can be used with the + * {@link ContextConfiguration#initializers()} to trigger loading of {@link ConfigData} + * such as {@literal application.properties}. + * + * @author Phillip Webb + * @since 2.4.0 + * @see ConfigDataEnvironmentPostProcessor + */ +public class ConfigDataApplicationContextInitializer + implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + RandomValuePropertySource.addToEnvironment(environment); + new ConfigDataProcessor().addPropertySources(environment, applicationContext); + DefaultPropertiesPropertySource.moveToEnd(environment); + } + + private static class ConfigDataProcessor extends ConfigDataEnvironmentPostProcessor { + + ConfigDataProcessor() { + super(Supplier::get); + } + + void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { + addPropertySources(environment, resourceLoader, null); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializer.java index b94707093360..8c0bfbd8434d 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -16,7 +16,6 @@ package org.springframework.boot.test.context; -import org.springframework.boot.context.config.ConfigFileApplicationListener; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.ContextConfiguration; @@ -28,14 +27,16 @@ * * @author Phillip Webb * @since 1.4.0 - * @see ConfigFileApplicationListener + * @see org.springframework.boot.context.config.ConfigFileApplicationListener + * @deprecated since 2.4.0 in favor of {@link ConfigDataApplicationContextInitializer} */ +@Deprecated public class ConfigFileApplicationContextInitializer implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { - new ConfigFileApplicationListener() { + new org.springframework.boot.context.config.ConfigFileApplicationListener() { public void apply() { addPropertySources(applicationContext.getEnvironment(), applicationContext); addPostProcessors(applicationContext); diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerTests.java new file mode 100644 index 000000000000..6c0e03ce6f77 --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.context; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ConfigDataApplicationContextInitializer}. + * + * @author Phillip Webb + */ +@ExtendWith(SpringExtension.class) +@DirtiesContext +@ContextConfiguration(classes = ConfigDataApplicationContextInitializerTests.Config.class, + initializers = ConfigDataApplicationContextInitializer.class) +class ConfigDataApplicationContextInitializerTests { + + @Autowired + private Environment environment; + + @Test + void initializerPopulatesEnvironment() { + assertThat(this.environment.getProperty("foo")).isEqualTo("bucket"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + } + +} diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerWithLegacySwitchTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerWithLegacySwitchTests.java new file mode 100644 index 000000000000..9247c3449aaa --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializerWithLegacySwitchTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.context; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ConfigDataApplicationContextInitializer}. + * + * @author Phillip Webb + */ +@ExtendWith(SpringExtension.class) +@DirtiesContext +@TestPropertySource(properties = "spring.config.use-legacy-processing=true") +@ContextConfiguration(classes = ConfigDataApplicationContextInitializerWithLegacySwitchTests.Config.class, + initializers = ConfigDataApplicationContextInitializer.class) +class ConfigDataApplicationContextInitializerWithLegacySwitchTests { + + @Autowired + private Environment environment; + + @Test + void initializerPopulatesEnvironment() { + assertThat(this.environment.getProperty("foo")).isEqualTo("bucket"); + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + } + +} diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializerTests.java index 045549480711..944283909304 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializerTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ConfigFileApplicationContextInitializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -33,6 +33,8 @@ * * @author Phillip Webb */ +@Deprecated +@SuppressWarnings("deprecation") @ExtendWith(SpringExtension.class) @DirtiesContext @ContextConfiguration(classes = ConfigFileApplicationContextInitializerTests.Config.class, diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 63046533e696..a5210f09610e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -237,7 +236,7 @@ public class SpringApplication { private Map defaultProperties; - private Set additionalProfiles = new HashSet<>(); + private Set additionalProfiles = Collections.emptySet(); private boolean allowBeanDefinitionOverriding; @@ -352,6 +351,7 @@ private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); DefaultPropertiesPropertySource.moveToEnd(environment); + configureAdditionalProfiles(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, @@ -527,9 +527,16 @@ protected void configurePropertySources(ConfigurableEnvironment environment, Str * @see org.springframework.boot.context.config.ConfigFileApplicationListener */ protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { - Set profiles = new LinkedHashSet<>(this.additionalProfiles); - profiles.addAll(Arrays.asList(environment.getActiveProfiles())); - environment.setActiveProfiles(StringUtils.toStringArray(profiles)); + } + + private void configureAdditionalProfiles(ConfigurableEnvironment environment) { + if (!CollectionUtils.isEmpty(this.additionalProfiles)) { + Set profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles())); + if (!profiles.containsAll(this.additionalProfiles)) { + profiles.addAll(this.additionalProfiles); + environment.setActiveProfiles(StringUtils.toStringArray(profiles)); + } + } } private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { @@ -1043,7 +1050,15 @@ public void setDefaultProperties(Properties defaultProperties) { * @param profiles the additional profiles to set */ public void setAdditionalProfiles(String... profiles) { - this.additionalProfiles = new LinkedHashSet<>(Arrays.asList(profiles)); + this.additionalProfiles = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(profiles))); + } + + /** + * Return an immutable set of any additional profiles in use. + * @return the additional profiles + */ + public Set getAdditionalProfiles() { + return this.additionalProfiles; } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java index ee60ef6fb70f..a0460d4416bf 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java @@ -22,8 +22,10 @@ import java.util.Map; import java.util.Properties; +import org.apache.commons.logging.Log; + import org.springframework.boot.SpringApplication; -import org.springframework.boot.context.config.ConfigFileApplicationListener; +import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.json.JsonParser; @@ -92,14 +94,36 @@ public class CloudFoundryVcapEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered, ApplicationListener { - private static final DeferredLog logger = new DeferredLog(); - private static final String VCAP_APPLICATION = "VCAP_APPLICATION"; private static final String VCAP_SERVICES = "VCAP_SERVICES"; + private final Log logger; + + private final boolean switchableLogger; + // Before ConfigFileApplicationListener so values there can use these ones - private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1; + private int order = ConfigDataEnvironmentPostProcessor.ORDER - 1; + + /** + * Create a new {@link CloudFoundryVcapEnvironmentPostProcessor} instance. + * @deprecated since 2.4.0 in favor of + * {@link #CloudFoundryVcapEnvironmentPostProcessor(Log)} + */ + @Deprecated + public CloudFoundryVcapEnvironmentPostProcessor() { + this.logger = new DeferredLog(); + this.switchableLogger = true; + } + + /** + * Create a new {@link CloudFoundryVcapEnvironmentPostProcessor} instance. + * @param logger the logger to use + */ + public CloudFoundryVcapEnvironmentPostProcessor(Log logger) { + this.logger = logger; + this.switchableLogger = false; + } public void setOrder(int order) { this.order = order; @@ -128,9 +152,17 @@ public void postProcessEnvironment(ConfigurableEnvironment environment, SpringAp } } + /** + * Event listener used to switch logging. + * @deprecated since 2.4.0 in favor of only using {@link EnvironmentPostProcessor} + * callbacks + */ + @Deprecated @Override public void onApplicationEvent(ApplicationPreparedEvent event) { - logger.switchTo(CloudFoundryVcapEnvironmentPostProcessor.class); + if (this.switchableLogger) { + ((DeferredLog) this.logger).switchTo(CloudFoundryVcapEnvironmentPostProcessor.class); + } } private void addWithPrefix(Properties properties, Properties other, String prefix) { @@ -148,7 +180,7 @@ private Properties getPropertiesFromApplication(Environment environment, JsonPar extractPropertiesFromApplication(properties, map); } catch (Exception ex) { - logger.error("Could not parse VCAP_APPLICATION", ex); + this.logger.error("Could not parse VCAP_APPLICATION", ex); } return properties; } @@ -161,7 +193,7 @@ private Properties getPropertiesFromServices(Environment environment, JsonParser extractPropertiesFromServices(properties, map); } catch (Exception ex) { - logger.error("Could not parse VCAP_SERVICES", ex); + this.logger.error("Could not parse VCAP_SERVICES", ex); } return properties; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java index 5796920ed040..931f021bbe28 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -20,6 +20,7 @@ import org.springframework.boot.ansi.AnsiOutput.Enabled; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.env.EnvironmentPostProcessorApplicationListener; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; @@ -46,8 +47,8 @@ public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { @Override public int getOrder() { - // Apply after ConfigFileApplicationListener has called EnvironmentPostProcessors - return ConfigFileApplicationListener.DEFAULT_ORDER + 1; + // Apply after EnvironmentPostProcessorApplicationListener + return EnvironmentPostProcessorApplicationListener.DEFAULT_ORDER + 1; } } 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 new file mode 100644 index 000000000000..28ec4915e71e --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.context.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.util.Assert; + +/** + * Configuration data that has been loaded from an external {@link ConfigDataLocation + * location} and may ultimately contribute {@link PropertySource property sources} to + * Spring's {@link Environment}. + * + * @author Phillip Webb + * @author Madhura Bhave + * @since 2.4.0 + * @see ConfigDataLocationResolver + * @see ConfigDataLoader + */ +public final class ConfigData { + + private final List> propertySources; + + private final Set