Skip to content

Commit

Permalink
Add ConfigDataEnvironmentUpdateListener support
Browse files Browse the repository at this point in the history
Add an overloaded `ConfigDataEnvironmentPostProcessor.applyTo` method
that accepts a listener that can used to track the updates that were
applied to the `Environment`.

The listener can be used to track the which `ConfigDataLocation` and
the `ConfigDataResource` were used to add a `PropertySource`. The lister
can also be used to tell which profiles were applied.

This enhancement is being added in a patch release because it's will
be useful for Spring Cloud 2020.0.0.

Closes gh-24504
  • Loading branch information
philwebb committed Dec 16, 2020
1 parent 5e1a69e commit 38e4c2a
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 24 deletions.
Expand Up @@ -119,6 +119,8 @@ class ConfigDataEnvironment {

private final Collection<String> additionalProfiles;

private final ConfigDataEnvironmentUpdateListener environmentUpdateListener;

private final ConfigDataLoaders loaders;

private final ConfigDataEnvironmentContributors contributors;
Expand All @@ -130,9 +132,13 @@ class ConfigDataEnvironment {
* @param environment the Spring {@link Environment}.
* @param resourceLoader {@link ResourceLoader} to load resource locations
* @param additionalProfiles any additional profiles to activate
* @param environmentUpdateListener optional
* {@link ConfigDataEnvironmentUpdateListener} that can be used to track
* {@link Environment} updates.
*/
ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles,
ConfigDataEnvironmentUpdateListener environmentUpdateListener) {
Binder binder = Binder.get(environment);
UseLegacyConfigProcessingException.throwIfRequested(binder);
this.logFactory = logFactory;
Expand All @@ -143,6 +149,8 @@ class ConfigDataEnvironment {
this.environment = environment;
this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
this.additionalProfiles = additionalProfiles;
this.environmentUpdateListener = (environmentUpdateListener != null) ? environmentUpdateListener
: ConfigDataEnvironmentUpdateListener.NONE;
this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext);
this.contributors = createContributors(binder);
}
Expand Down Expand Up @@ -301,16 +309,18 @@ private void applyToEnvironment(ConfigDataEnvironmentContributors contributors,
MutablePropertySources propertySources = this.environment.getPropertySources();
this.logger.trace("Applying config data environment contributions");
for (ConfigDataEnvironmentContributor contributor : contributors) {
if (contributor.getKind() == ConfigDataEnvironmentContributor.Kind.BOUND_IMPORT
&& contributor.getPropertySource() != null) {
PropertySource<?> propertySource = contributor.getPropertySource();
if (contributor.getKind() == ConfigDataEnvironmentContributor.Kind.BOUND_IMPORT && propertySource != null) {
if (!contributor.isActive(activationContext)) {
this.logger.trace(LogMessage.format("Skipping inactive property source '%s'",
contributor.getPropertySource().getName()));
this.logger.trace(
LogMessage.format("Skipping inactive property source '%s'", propertySource.getName()));
}
else {
this.logger.trace(LogMessage.format("Adding imported property source '%s'",
contributor.getPropertySource().getName()));
propertySources.addLast(contributor.getPropertySource());
this.logger
.trace(LogMessage.format("Adding imported property source '%s'", propertySource.getName()));
propertySources.addLast(propertySource);
this.environmentUpdateListener.onPropertySourceAdded(propertySource, contributor.getLocation(),
contributor.getResource());
}
}
}
Expand All @@ -320,6 +330,7 @@ private void applyToEnvironment(ConfigDataEnvironmentContributors contributors,
this.environment.setDefaultProfiles(StringUtils.toStringArray(profiles.getDefault()));
this.logger.trace(LogMessage.format("Setting active profiles: %s", profiles.getActive()));
this.environment.setActiveProfiles(StringUtils.toStringArray(profiles.getActive()));
this.environmentUpdateListener.onSetProfiles(profiles);
}

private void checkForInvalidProperties(ConfigDataEnvironmentContributors contributors) {
Expand Down
Expand Up @@ -63,11 +63,20 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces

private final ConfigurableBootstrapContext bootstrapContext;

private final ConfigDataEnvironmentUpdateListener environmentUpdateListener;

public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory,
ConfigurableBootstrapContext bootstrapContext) {
this(logFactory, bootstrapContext, null);
}

public ConfigDataEnvironmentPostProcessor(DeferredLogFactory logFactory,
ConfigurableBootstrapContext bootstrapContext,
ConfigDataEnvironmentUpdateListener environmentUpdateListener) {
this.logFactory = logFactory;
this.logger = logFactory.getLog(getClass());
this.bootstrapContext = bootstrapContext;
this.environmentUpdateListener = environmentUpdateListener;
}

@Override
Expand Down Expand Up @@ -97,7 +106,7 @@ void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader
ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection<String> additionalProfiles) {
return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
additionalProfiles);
additionalProfiles, this.environmentUpdateListener);
}

private void postProcessUsingLegacyApplicationListener(ConfigurableEnvironment environment,
Expand Down Expand Up @@ -154,6 +163,29 @@ public static void applyTo(ConfigurableEnvironment environment, ResourceLoader r
postProcessor.postProcessEnvironment(environment, resourceLoader, additionalProfiles);
}

/**
* Apply {@link ConfigData} post-processing to an existing {@link Environment}. This
* method can be useful when working with an {@link Environment} that has been created
* directly and not necessarily as part of a {@link SpringApplication}.
* @param environment the environment to apply {@link ConfigData} to
* @param resourceLoader the resource loader to use
* @param bootstrapContext the bootstrap context to use or {@code null} to use a
* throw-away context
* @param additionalProfiles any additional profiles that should be applied
* @param environmentUpdateListener optional
* {@link ConfigDataEnvironmentUpdateListener} that can be used to track
* {@link Environment} updates.
*/
public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
ConfigurableBootstrapContext bootstrapContext, Collection<String> additionalProfiles,
ConfigDataEnvironmentUpdateListener environmentUpdateListener) {
DeferredLogFactory logFactory = Supplier::get;
bootstrapContext = (bootstrapContext != null) ? bootstrapContext : new DefaultBootstrapContext();
ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(logFactory,
bootstrapContext, environmentUpdateListener);
postProcessor.postProcessEnvironment(environment, resourceLoader, additionalProfiles);
}

@SuppressWarnings("deprecation")
static class LegacyConfigFileApplicationListener extends ConfigFileApplicationListener {

Expand Down
@@ -0,0 +1,56 @@
/*
* 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.EventListener;

import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;

/**
* {@link EventListener} to listen to {@link Environment} updates triggered by the
* {@link ConfigDataEnvironmentPostProcessor}.
*
* @author Phillip Webb
* @since 2.4.2
*/
public interface ConfigDataEnvironmentUpdateListener extends EventListener {

/**
* A {@link ConfigDataEnvironmentUpdateListener} that does nothing.
*/
ConfigDataEnvironmentUpdateListener NONE = new ConfigDataEnvironmentUpdateListener() {
};

/**
* Called when a new {@link PropertySource} is added to the {@link Environment}.
* @param propertySource the {@link PropertySource} that was added
* @param location the original {@link ConfigDataLocation} of the source.
* @param resource the {@link ConfigDataResource} of the source.
*/
default void onPropertySourceAdded(PropertySource<?> propertySource, ConfigDataLocation location,
ConfigDataResource resource) {
}

/**
* Called when {@link Environment} profiles are set.
* @param profiles the profiles being set
*/
default void onSetProfiles(Profiles profiles) {
}

}
Expand Up @@ -16,6 +16,7 @@

package org.springframework.boot.context.config;

import java.util.Collections;
import java.util.Set;
import java.util.function.Supplier;

Expand Down Expand Up @@ -119,9 +120,13 @@ void postProcessEnvironmentWhenUseLegacyProcessingSwitchesToLegacyMethod() {
@Test
void applyToAppliesPostProcessing() {
int before = this.environment.getPropertySources().size();
ConfigDataEnvironmentPostProcessor.applyTo(this.environment, null, null, "dev");
TestConfigDataEnvironmentUpdateListener listener = new TestConfigDataEnvironmentUpdateListener();
ConfigDataEnvironmentPostProcessor.applyTo(this.environment, null, null, Collections.singleton("dev"),
listener);
assertThat(this.environment.getPropertySources().size()).isGreaterThan(before);
assertThat(this.environment.getActiveProfiles()).containsExactly("dev");
assertThat(listener.getAddedPropertySources()).hasSizeGreaterThan(0);
assertThat(listener.getProfiles().getActive()).containsExactly("dev");
}

}

0 comments on commit 38e4c2a

Please sign in to comment.