Skip to content

Commit

Permalink
Provide ConfigFileApplicationListener replacement
Browse files Browse the repository at this point in the history
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 <mbhave@vmware.com>
  • Loading branch information
philwebb and mbhave committed Jul 23, 2020
1 parent 44f1836 commit 3352024
Show file tree
Hide file tree
Showing 96 changed files with 8,130 additions and 168 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -71,7 +72,7 @@ private Collection<ApplicationContextInitializer<?>> getInitializers() {
private Collection<ApplicationListener<?>> getListeners() {
List<ApplicationListener<?>> 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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ConfigurableApplicationContext> {

@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);
}

}

}
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -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<ConfigurableApplicationContext> {

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
new ConfigFileApplicationListener() {
new org.springframework.boot.context.config.ConfigFileApplicationListener() {
public void apply() {
addPropertySources(applicationContext.getEnvironment(), applicationContext);
addPostProcessors(applicationContext);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {

}

}
Original file line number Diff line number Diff line change
@@ -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 {

}

}
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -33,6 +33,8 @@
*
* @author Phillip Webb
*/
@Deprecated
@SuppressWarnings("deprecation")
@ExtendWith(SpringExtension.class)
@DirtiesContext
@ContextConfiguration(classes = ConfigFileApplicationContextInitializerTests.Config.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -237,7 +236,7 @@ public class SpringApplication {

private Map<String, Object> defaultProperties;

private Set<String> additionalProfiles = new HashSet<>();
private Set<String> additionalProfiles = Collections.emptySet();

private boolean allowBeanDefinitionOverriding;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<String> 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<String> 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) {
Expand Down Expand Up @@ -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<String> getAdditionalProfiles() {
return this.additionalProfiles;
}

/**
Expand Down
Loading

0 comments on commit 3352024

Please sign in to comment.