Use environment conversion service when resolving placeholders

Closes gh-39944
This commit is contained in:
Moritz Halbritter 2024-03-28 09:48:55 +01:00
parent 9c68ce5900
commit 0722ac796b
9 changed files with 228 additions and 126 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -172,20 +172,23 @@ class ConfigDataEnvironment {
else { else {
this.logger.trace(LogMessage.format("Creating wrapped config data contributor for '%s'", this.logger.trace(LogMessage.format("Creating wrapped config data contributor for '%s'",
propertySource.getName())); propertySource.getName()));
contributors.add(ConfigDataEnvironmentContributor.ofExisting(propertySource)); contributors.add(ConfigDataEnvironmentContributor.ofExisting(propertySource,
this.environment.getConversionService()));
} }
} }
contributors.addAll(getInitialImportContributors(binder)); contributors.addAll(getInitialImportContributors(binder));
if (defaultPropertySource != null) { if (defaultPropertySource != null) {
this.logger.trace("Creating wrapped config data contributor for default property source"); this.logger.trace("Creating wrapped config data contributor for default property source");
contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource)); contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource,
this.environment.getConversionService()));
} }
return createContributors(contributors); return createContributors(contributors);
} }
protected ConfigDataEnvironmentContributors createContributors( protected ConfigDataEnvironmentContributors createContributors(
List<ConfigDataEnvironmentContributor> contributors) { List<ConfigDataEnvironmentContributor> contributors) {
return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors); return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors,
this.environment.getConversionService());
} }
ConfigDataEnvironmentContributors getContributors() { ConfigDataEnvironmentContributors getContributors() {
@ -215,7 +218,7 @@ class ConfigDataEnvironment {
private ConfigDataEnvironmentContributor createInitialImportContributor(ConfigDataLocation location) { private ConfigDataEnvironmentContributor createInitialImportContributor(ConfigDataLocation location) {
this.logger.trace(LogMessage.format("Adding initial config data import from location '%s'", location)); this.logger.trace(LogMessage.format("Adding initial config data import from location '%s'", location));
return ConfigDataEnvironmentContributor.ofInitialImport(location); return ConfigDataEnvironmentContributor.ofInitialImport(location, this.environment.getConversionService());
} }
/** /**
@ -288,7 +291,7 @@ class ConfigDataEnvironment {
private Collection<? extends String> getIncludedProfiles(ConfigDataEnvironmentContributors contributors, private Collection<? extends String> getIncludedProfiles(ConfigDataEnvironmentContributors contributors,
ConfigDataActivationContext activationContext) { ConfigDataActivationContext activationContext) {
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, activationContext, null, true); contributors, activationContext, null, true, this.environment.getConversionService());
Set<String> result = new LinkedHashSet<>(); Set<String> result = new LinkedHashSet<>();
for (ConfigDataEnvironmentContributor contributor : contributors) { for (ConfigDataEnvironmentContributor contributor : contributors) {
ConfigurationPropertySource source = contributor.getConfigurationPropertySource(); ConfigurationPropertySource source = contributor.getConfigurationPropertySource();

View File

@ -29,6 +29,7 @@ import java.util.stream.StreamSupport;
import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver; import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -74,6 +75,8 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
private final Kind kind; private final Kind kind;
private final ConversionService conversionService;
/** /**
* Create a new {@link ConfigDataEnvironmentContributor} instance. * Create a new {@link ConfigDataEnvironmentContributor} instance.
* @param kind the contributor kind * @param kind the contributor kind
@ -87,11 +90,13 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
* @param properties the config data properties or {@code null} * @param properties the config data properties or {@code null}
* @param configDataOptions any config data options that should apply * @param configDataOptions any config data options that should apply
* @param children the children of this contributor at each {@link ImportPhase} * @param children the children of this contributor at each {@link ImportPhase}
* @param conversionService the conversion service to use
*/ */
ConfigDataEnvironmentContributor(Kind kind, ConfigDataLocation location, ConfigDataResource resource, ConfigDataEnvironmentContributor(Kind kind, ConfigDataLocation location, ConfigDataResource resource,
boolean fromProfileSpecificImport, PropertySource<?> propertySource, boolean fromProfileSpecificImport, PropertySource<?> propertySource,
ConfigurationPropertySource configurationPropertySource, ConfigDataProperties properties, ConfigurationPropertySource configurationPropertySource, ConfigDataProperties properties,
ConfigData.Options configDataOptions, Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children) { ConfigData.Options configDataOptions, Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children,
ConversionService conversionService) {
this.kind = kind; this.kind = kind;
this.location = location; this.location = location;
this.resource = resource; this.resource = resource;
@ -101,6 +106,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
this.configurationPropertySource = configurationPropertySource; this.configurationPropertySource = configurationPropertySource;
this.configDataOptions = (configDataOptions != null) ? configDataOptions : ConfigData.Options.NONE; this.configDataOptions = (configDataOptions != null) ? configDataOptions : ConfigData.Options.NONE;
this.children = (children != null) ? children : Collections.emptyMap(); this.children = (children != null) ? children : Collections.emptyMap();
this.conversionService = conversionService;
} }
/** /**
@ -171,7 +177,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
ConfigDataEnvironmentContributor withoutConfigDataOption(ConfigData.Option option) { ConfigDataEnvironmentContributor withoutConfigDataOption(ConfigData.Option option) {
return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource, return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource,
this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties, this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties,
this.configDataOptions.without(option), this.children); this.configDataOptions.without(option), this.children, this.conversionService);
} }
/** /**
@ -235,7 +241,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
ConfigDataActivationContext activationContext) { ConfigDataActivationContext activationContext) {
Iterable<ConfigurationPropertySource> sources = Collections.singleton(getConfigurationPropertySource()); Iterable<ConfigurationPropertySource> sources = Collections.singleton(getConfigurationPropertySource());
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, activationContext, this, true); contributors, activationContext, this, true, this.conversionService);
Binder binder = new Binder(sources, placeholdersResolver, null, null, null); Binder binder = new Binder(sources, placeholdersResolver, null, null, null);
ConfigDataProperties properties = ConfigDataProperties.get(binder); ConfigDataProperties properties = ConfigDataProperties.get(binder);
if (properties != null && this.configDataOptions.contains(ConfigData.Option.IGNORE_IMPORTS)) { if (properties != null && this.configDataOptions.contains(ConfigData.Option.IGNORE_IMPORTS)) {
@ -243,7 +249,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
} }
return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, this.location, this.resource, return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, this.location, this.resource,
this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, properties, this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, properties,
this.configDataOptions, null); this.configDataOptions, null, this.conversionService);
} }
/** /**
@ -262,7 +268,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
} }
return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource, return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource,
this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties, this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties,
this.configDataOptions, updatedChildren); this.configDataOptions, updatedChildren, this.conversionService);
} }
private void moveProfileSpecific(Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children) { private void moveProfileSpecific(Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children) {
@ -337,7 +343,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
}); });
return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource, return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource,
this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties, this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties,
this.configDataOptions, updatedChildren); this.configDataOptions, updatedChildren, this.conversionService);
} }
@Override @Override
@ -370,12 +376,15 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
/** /**
* Factory method to create a {@link Kind#ROOT root} contributor. * Factory method to create a {@link Kind#ROOT root} contributor.
* @param contributors the immediate children of the root * @param contributors the immediate children of the root
* @param conversionService the conversion service to use
* @return a new {@link ConfigDataEnvironmentContributor} instance * @return a new {@link ConfigDataEnvironmentContributor} instance
*/ */
static ConfigDataEnvironmentContributor of(List<ConfigDataEnvironmentContributor> contributors) { static ConfigDataEnvironmentContributor of(List<ConfigDataEnvironmentContributor> contributors,
ConversionService conversionService) {
Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children = new LinkedHashMap<>(); Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children = new LinkedHashMap<>();
children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors)); children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors));
return new ConfigDataEnvironmentContributor(Kind.ROOT, null, null, false, null, null, null, null, children); return new ConfigDataEnvironmentContributor(Kind.ROOT, null, null, false, null, null, null, null, children,
conversionService);
} }
/** /**
@ -383,13 +392,15 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
* This contributor is used to trigger initial imports of additional contributors. It * This contributor is used to trigger initial imports of additional contributors. It
* does not contribute any properties itself. * does not contribute any properties itself.
* @param initialImport the initial import location (with placeholders resolved) * @param initialImport the initial import location (with placeholders resolved)
* @param conversionService the conversion service to use
* @return a new {@link ConfigDataEnvironmentContributor} instance * @return a new {@link ConfigDataEnvironmentContributor} instance
*/ */
static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initialImport) { static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initialImport,
ConversionService conversionService) {
List<ConfigDataLocation> imports = Collections.singletonList(initialImport); List<ConfigDataLocation> imports = Collections.singletonList(initialImport);
ConfigDataProperties properties = new ConfigDataProperties(imports, null); ConfigDataProperties properties = new ConfigDataProperties(imports, null);
return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT, null, null, false, null, null, properties, return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT, null, null, false, null, null, properties,
null, null); null, null, conversionService);
} }
/** /**
@ -397,11 +408,13 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
* property source. The contributor provides access to existing properties, but * property source. The contributor provides access to existing properties, but
* doesn't actively import any additional contributors. * doesn't actively import any additional contributors.
* @param propertySource the property source to wrap * @param propertySource the property source to wrap
* @param conversionService the conversion service to use
* @return a new {@link ConfigDataEnvironmentContributor} instance * @return a new {@link ConfigDataEnvironmentContributor} instance
*/ */
static ConfigDataEnvironmentContributor ofExisting(PropertySource<?> propertySource) { static ConfigDataEnvironmentContributor ofExisting(PropertySource<?> propertySource,
ConversionService conversionService) {
return new ConfigDataEnvironmentContributor(Kind.EXISTING, null, null, false, propertySource, return new ConfigDataEnvironmentContributor(Kind.EXISTING, null, null, false, propertySource,
ConfigurationPropertySource.from(propertySource), null, null, null); ConfigurationPropertySource.from(propertySource), null, null, null, conversionService);
} }
/** /**
@ -413,26 +426,30 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
* @param profileSpecific if the contributor is from a profile specific import * @param profileSpecific if the contributor is from a profile specific import
* @param configData the config data * @param configData the config data
* @param propertySourceIndex the index of the property source that should be used * @param propertySourceIndex the index of the property source that should be used
* @param conversionService the conversion service to use
* @return a new {@link ConfigDataEnvironmentContributor} instance * @return a new {@link ConfigDataEnvironmentContributor} instance
*/ */
static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataLocation location, ConfigDataResource resource, static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataLocation location, ConfigDataResource resource,
boolean profileSpecific, ConfigData configData, int propertySourceIndex) { boolean profileSpecific, ConfigData configData, int propertySourceIndex,
ConversionService conversionService) {
PropertySource<?> propertySource = configData.getPropertySources().get(propertySourceIndex); PropertySource<?> propertySource = configData.getPropertySources().get(propertySourceIndex);
ConfigData.Options options = configData.getOptions(propertySource); ConfigData.Options options = configData.getOptions(propertySource);
ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource); ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource);
return new ConfigDataEnvironmentContributor(Kind.UNBOUND_IMPORT, location, resource, profileSpecific, return new ConfigDataEnvironmentContributor(Kind.UNBOUND_IMPORT, location, resource, profileSpecific,
propertySource, configurationPropertySource, null, options, null); propertySource, configurationPropertySource, null, options, null, conversionService);
} }
/** /**
* Factory method to create an {@link Kind#EMPTY_LOCATION empty location} contributor. * Factory method to create an {@link Kind#EMPTY_LOCATION empty location} contributor.
* @param location the location of this contributor * @param location the location of this contributor
* @param profileSpecific if the contributor is from a profile specific import * @param profileSpecific if the contributor is from a profile specific import
* @param conversionService the conversion service to use
* @return a new {@link ConfigDataEnvironmentContributor} instance * @return a new {@link ConfigDataEnvironmentContributor} instance
*/ */
static ConfigDataEnvironmentContributor ofEmptyLocation(ConfigDataLocation location, boolean profileSpecific) { static ConfigDataEnvironmentContributor ofEmptyLocation(ConfigDataLocation location, boolean profileSpecific,
ConversionService conversionService) {
return new ConfigDataEnvironmentContributor(Kind.EMPTY_LOCATION, location, null, profileSpecific, null, null, return new ConfigDataEnvironmentContributor(Kind.EMPTY_LOCATION, location, null, profileSpecific, null, null,
null, EMPTY_LOCATION_OPTIONS, null); null, EMPTY_LOCATION_OPTIONS, null, conversionService);
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.
import org.springframework.boot.context.properties.bind.PlaceholdersResolver; import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.origin.OriginLookup;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.util.PropertyPlaceholderHelper; import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.SystemPropertyUtils; import org.springframework.util.SystemPropertyUtils;
@ -30,6 +31,7 @@ import org.springframework.util.SystemPropertyUtils;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
* @author Moritz Halbritter
*/ */
class ConfigDataEnvironmentContributorPlaceholdersResolver implements PlaceholdersResolver { class ConfigDataEnvironmentContributorPlaceholdersResolver implements PlaceholdersResolver {
@ -43,13 +45,16 @@ class ConfigDataEnvironmentContributorPlaceholdersResolver implements Placeholde
private final ConfigDataEnvironmentContributor activeContributor; private final ConfigDataEnvironmentContributor activeContributor;
private final ConversionService conversionService;
ConfigDataEnvironmentContributorPlaceholdersResolver(Iterable<ConfigDataEnvironmentContributor> contributors, ConfigDataEnvironmentContributorPlaceholdersResolver(Iterable<ConfigDataEnvironmentContributor> contributors,
ConfigDataActivationContext activationContext, ConfigDataEnvironmentContributor activeContributor, ConfigDataActivationContext activationContext, ConfigDataEnvironmentContributor activeContributor,
boolean failOnResolveFromInactiveContributor) { boolean failOnResolveFromInactiveContributor, ConversionService conversionService) {
this.contributors = contributors; this.contributors = contributors;
this.activationContext = activationContext; this.activationContext = activationContext;
this.activeContributor = activeContributor; this.activeContributor = activeContributor;
this.failOnResolveFromInactiveContributor = failOnResolveFromInactiveContributor; this.failOnResolveFromInactiveContributor = failOnResolveFromInactiveContributor;
this.conversionService = conversionService;
this.helper = new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, this.helper = new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true); SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
} }
@ -77,7 +82,7 @@ class ConfigDataEnvironmentContributorPlaceholdersResolver implements Placeholde
} }
result = (result != null) ? result : value; result = (result != null) ? result : value;
} }
return (result != null) ? String.valueOf(result) : null; return (result != null) ? convertValueIfNecessary(result) : null;
} }
private boolean isActive(ConfigDataEnvironmentContributor contributor) { private boolean isActive(ConfigDataEnvironmentContributor contributor) {
@ -91,4 +96,11 @@ class ConfigDataEnvironmentContributorPlaceholdersResolver implements Placeholde
.isActive(this.activationContext); .isActive(this.activationContext);
} }
private String convertValueIfNecessary(Object value) {
if (value instanceof String string) {
return string;
}
return this.conversionService.convert(value, String.class);
}
} }

View File

@ -39,6 +39,7 @@ import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.log.LogMessage; import org.springframework.core.log.LogMessage;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -59,24 +60,29 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
private final ConfigurableBootstrapContext bootstrapContext; private final ConfigurableBootstrapContext bootstrapContext;
private final ConversionService conversionService;
/** /**
* Create a new {@link ConfigDataEnvironmentContributors} instance. * Create a new {@link ConfigDataEnvironmentContributors} instance.
* @param logFactory the log factory * @param logFactory the log factory
* @param bootstrapContext the bootstrap context * @param bootstrapContext the bootstrap context
* @param contributors the initial set of contributors * @param contributors the initial set of contributors
* @param conversionService the conversion service to use
*/ */
ConfigDataEnvironmentContributors(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, ConfigDataEnvironmentContributors(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
List<ConfigDataEnvironmentContributor> contributors) { List<ConfigDataEnvironmentContributor> contributors, ConversionService conversionService) {
this.logger = logFactory.getLog(getClass()); this.logger = logFactory.getLog(getClass());
this.bootstrapContext = bootstrapContext; this.bootstrapContext = bootstrapContext;
this.root = ConfigDataEnvironmentContributor.of(contributors); this.root = ConfigDataEnvironmentContributor.of(contributors, conversionService);
this.conversionService = conversionService;
} }
private ConfigDataEnvironmentContributors(Log logger, ConfigurableBootstrapContext bootstrapContext, private ConfigDataEnvironmentContributors(Log logger, ConfigurableBootstrapContext bootstrapContext,
ConfigDataEnvironmentContributor root) { ConfigDataEnvironmentContributor root, ConversionService conversionService) {
this.logger = logger; this.logger = logger;
this.bootstrapContext = bootstrapContext; this.bootstrapContext = bootstrapContext;
this.root = root; this.root = root;
this.conversionService = conversionService;
} }
/** /**
@ -104,7 +110,7 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
if (contributor.getKind() == Kind.UNBOUND_IMPORT) { if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(result, activationContext); ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(result, activationContext);
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext, result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, bound)); result.getRoot().withReplacement(contributor, bound), this.conversionService);
continue; continue;
} }
ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext( ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
@ -118,7 +124,7 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase, ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
asContributors(imported)); asContributors(imported));
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext, result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, contributorAndChildren)); result.getRoot().withReplacement(contributor, contributorAndChildren), this.conversionService);
processed++; processed++;
} }
} }
@ -161,12 +167,13 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
ConfigDataResource resource = resolutionResult.getResource(); ConfigDataResource resource = resolutionResult.getResource();
boolean profileSpecific = resolutionResult.isProfileSpecific(); boolean profileSpecific = resolutionResult.isProfileSpecific();
if (data.getPropertySources().isEmpty()) { if (data.getPropertySources().isEmpty()) {
contributors.add(ConfigDataEnvironmentContributor.ofEmptyLocation(location, profileSpecific)); contributors.add(ConfigDataEnvironmentContributor.ofEmptyLocation(location, profileSpecific,
this.conversionService));
} }
else { else {
for (int i = data.getPropertySources().size() - 1; i >= 0; i--) { for (int i = data.getPropertySources().size() - 1; i >= 0; i--) {
contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(location, resource, contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(location, resource,
profileSpecific, data, i)); profileSpecific, data, i, this.conversionService));
} }
} }
}); });
@ -214,7 +221,7 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
Iterable<ConfigurationPropertySource> sources = () -> getBinderSources( Iterable<ConfigurationPropertySource> sources = () -> getBinderSources(
filter.and((contributor) -> failOnInactiveSource || contributor.isActive(activationContext))); filter.and((contributor) -> failOnInactiveSource || contributor.isActive(activationContext)));
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(this.root, PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(this.root,
activationContext, null, failOnInactiveSource); activationContext, null, failOnInactiveSource, this.conversionService);
BindHandler bindHandler = !failOnInactiveSource ? null : new InactiveSourceChecker(activationContext); BindHandler bindHandler = !failOnInactiveSource ? null : new InactiveSourceChecker(activationContext);
return new Binder(sources, placeholdersResolver, null, null, bindHandler); return new Binder(sources, placeholdersResolver, null, null, bindHandler);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,6 +27,9 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup; import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.origin.PropertySourceOrigin; import org.springframework.boot.origin.PropertySourceOrigin;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
@ -38,42 +41,63 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Madhura Bhave * @author Madhura Bhave
* @author Moritz Halbritter
*/ */
class ConfigDataEnvironmentContributorPlaceholdersResolverTests { class ConfigDataEnvironmentContributorPlaceholdersResolverTests {
private final ConversionService conversionService = DefaultConversionService.getSharedInstance();
@Test @Test
void resolvePlaceholdersWhenNotStringReturnsResolved() { void resolvePlaceholdersWhenNotStringReturnsResolved() {
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
Collections.emptyList(), null, null, false); Collections.emptyList(), null, null, false, this.conversionService);
assertThat(resolver.resolvePlaceholders(123)).isEqualTo(123); assertThat(resolver.resolvePlaceholders(123)).isEqualTo(123);
} }
@Test @Test
void resolvePlaceholdersWhenNotFoundReturnsOriginal() { void resolvePlaceholdersWhenNotFoundReturnsOriginal() {
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
Collections.emptyList(), null, null, false); Collections.emptyList(), null, null, false, this.conversionService);
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("${test}"); assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("${test}");
} }
@Test @Test
void resolvePlaceholdersWhenFoundReturnsFirstMatch() { void resolvePlaceholdersWhenFoundReturnsFirstMatch() {
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(); List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>();
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true)); contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true,
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true)); this.conversionService));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), true)); contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true,
this.conversionService));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), true,
this.conversionService));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, true); contributors, null, null, true, this.conversionService);
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("t2"); assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("t2");
} }
@Test
void shouldUseConversionService() {
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(CustomValue.class, String.class, (input) -> "custom-value");
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>();
contributors.add(new TestConfigDataEnvironmentContributor(
new TestPropertySource("s1", Map.of("test", new CustomValue())), true, conversionService));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, true, conversionService);
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("custom-value");
}
@Test @Test
void resolvePlaceholdersWhenFoundInInactiveThrowsException() { void resolvePlaceholdersWhenFoundInInactiveThrowsException() {
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(); List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>();
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true)); contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true,
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true)); this.conversionService));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), false)); contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true,
this.conversionService));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), false,
this.conversionService));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, true); contributors, null, null, true, this.conversionService);
assertThatExceptionOfType(InactiveConfigDataAccessException.class) assertThatExceptionOfType(InactiveConfigDataAccessException.class)
.isThrownBy(() -> resolver.resolvePlaceholders("${test}")) .isThrownBy(() -> resolver.resolvePlaceholders("${test}"))
.satisfies(propertyNameAndOriginOf("test", "s3")); .satisfies(propertyNameAndOriginOf("test", "s3"));
@ -82,11 +106,14 @@ class ConfigDataEnvironmentContributorPlaceholdersResolverTests {
@Test @Test
void resolvePlaceholderWhenFoundInInactiveAndIgnoringReturnsResolved() { void resolvePlaceholderWhenFoundInInactiveAndIgnoringReturnsResolved() {
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(); List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>();
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true)); contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true,
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true)); this.conversionService));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), false)); contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true,
this.conversionService));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), false,
this.conversionService));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver( ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, false); contributors, null, null, false, this.conversionService);
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("t2"); assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("t2");
} }
@ -121,8 +148,9 @@ class ConfigDataEnvironmentContributorPlaceholdersResolverTests {
private final boolean active; private final boolean active;
protected TestConfigDataEnvironmentContributor(PropertySource<?> propertySource, boolean active) { protected TestConfigDataEnvironmentContributor(PropertySource<?> propertySource, boolean active,
super(Kind.ROOT, null, null, false, propertySource, null, null, null, null); ConversionService conversionService) {
super(Kind.ROOT, null, null, false, propertySource, null, null, null, null, conversionService);
this.active = active; this.active = active;
} }
@ -133,4 +161,8 @@ class ConfigDataEnvironmentContributorPlaceholdersResolverTests {
} }
private static final class CustomValue {
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,6 +31,8 @@ import org.springframework.boot.context.config.ConfigData.PropertySourceOptions;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase; import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase;
import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind; import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.mock.env.MockPropertySource; import org.springframework.mock.env.MockPropertySource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -50,15 +52,19 @@ class ConfigDataEnvironmentContributorTests {
private final ConfigDataActivationContext activationContext = new ConfigDataActivationContext( private final ConfigDataActivationContext activationContext = new ConfigDataActivationContext(
CloudPlatform.KUBERNETES, null); CloudPlatform.KUBERNETES, null);
private final ConversionService conversionService = DefaultConversionService.getSharedInstance();
@Test @Test
void getKindReturnsKind() { void getKindReturnsKind() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION,
this.conversionService);
assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT); assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT);
} }
@Test @Test
void isActiveWhenPropertiesIsNullReturnsTrue() { void isActiveWhenPropertiesIsNullReturnsTrue() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION,
this.conversionService);
assertThat(contributor.isActive(null)).isTrue(); assertThat(contributor.isActive(null)).isTrue();
} }
@ -85,14 +91,15 @@ class ConfigDataEnvironmentContributorTests {
ConfigData configData = new ConfigData(Collections.singleton(new MockPropertySource())); ConfigData configData = new ConfigData(Collections.singleton(new MockPropertySource()));
ConfigDataResource resource = mock(ConfigDataResource.class); ConfigDataResource resource = mock(ConfigDataResource.class);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION, ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION,
resource, false, configData, 0); resource, false, configData, 0, this.conversionService);
assertThat(contributor.getResource()).isSameAs(resource); assertThat(contributor.getResource()).isSameAs(resource);
} }
@Test @Test
void getPropertySourceReturnsPropertySource() { void getPropertySourceReturnsPropertySource() {
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource,
this.conversionService);
assertThat(contributor.getPropertySource()).isSameAs(propertySource); assertThat(contributor.getPropertySource()).isSameAs(propertySource);
} }
@ -102,7 +109,7 @@ class ConfigDataEnvironmentContributorTests {
propertySource.setProperty("spring", "boot"); propertySource.setProperty("spring", "boot");
ConfigData configData = new ConfigData(Collections.singleton(propertySource)); ConfigData configData = new ConfigData(Collections.singleton(propertySource));
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(null, null, ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(null, null,
false, configData, 0); false, configData, 0, this.conversionService);
assertThat(contributor.getConfigurationPropertySource() assertThat(contributor.getConfigurationPropertySource()
.getConfigurationProperty(ConfigurationPropertyName.of("spring")) .getConfigurationProperty(ConfigurationPropertyName.of("spring"))
.getValue()).isEqualTo("boot"); .getValue()).isEqualTo("boot");
@ -267,7 +274,8 @@ class ConfigDataEnvironmentContributorTests {
void ofCreatesRootContributor() { void ofCreatesRootContributor() {
ConfigDataEnvironmentContributor one = createBoundContributor("one"); ConfigDataEnvironmentContributor one = createBoundContributor("one");
ConfigDataEnvironmentContributor two = createBoundContributor("two"); ConfigDataEnvironmentContributor two = createBoundContributor("two");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.of(Arrays.asList(one, two)); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.of(Arrays.asList(one, two),
this.conversionService);
assertThat(contributor.getKind()).isEqualTo(Kind.ROOT); assertThat(contributor.getKind()).isEqualTo(Kind.ROOT);
assertThat(contributor.getResource()).isNull(); assertThat(contributor.getResource()).isNull();
assertThat(contributor.getImports()).isEmpty(); assertThat(contributor.getImports()).isEmpty();
@ -279,7 +287,8 @@ class ConfigDataEnvironmentContributorTests {
@Test @Test
void ofInitialImportCreatedInitialImportContributor() { void ofInitialImportCreatedInitialImportContributor() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION,
this.conversionService);
assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT); assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT);
assertThat(contributor.getResource()).isNull(); assertThat(contributor.getResource()).isNull();
assertThat(contributor.getImports()).containsExactly(TEST_LOCATION); assertThat(contributor.getImports()).containsExactly(TEST_LOCATION);
@ -294,7 +303,8 @@ class ConfigDataEnvironmentContributorTests {
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("spring.config.import", "test"); propertySource.setProperty("spring.config.import", "test");
propertySource.setProperty("spring.config.activate.on-cloud-platform", "cloudfoundry"); propertySource.setProperty("spring.config.activate.on-cloud-platform", "cloudfoundry");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource,
this.conversionService);
assertThat(contributor.getKind()).isEqualTo(Kind.EXISTING); assertThat(contributor.getKind()).isEqualTo(Kind.EXISTING);
assertThat(contributor.getResource()).isNull(); assertThat(contributor.getResource()).isNull();
assertThat(contributor.getImports()).isEmpty(); // Properties must not be bound assertThat(contributor.getImports()).isEmpty(); // Properties must not be bound
@ -311,7 +321,7 @@ class ConfigDataEnvironmentContributorTests {
propertySource.setProperty("spring.config.import", "test"); propertySource.setProperty("spring.config.import", "test");
ConfigData configData = new ConfigData(Collections.singleton(propertySource)); ConfigData configData = new ConfigData(Collections.singleton(propertySource));
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION, ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION,
resource, false, configData, 0); resource, false, configData, 0, this.conversionService);
assertThat(contributor.getKind()).isEqualTo(Kind.UNBOUND_IMPORT); assertThat(contributor.getKind()).isEqualTo(Kind.UNBOUND_IMPORT);
assertThat(contributor.getResource()).isSameAs(resource); assertThat(contributor.getResource()).isSameAs(resource);
assertThat(contributor.getImports()).isEmpty(); assertThat(contributor.getImports()).isEmpty();
@ -358,7 +368,7 @@ class ConfigDataEnvironmentContributorTests {
TestResource resource = new TestResource("a"); TestResource resource = new TestResource("a");
ConfigData configData = new ConfigData(Collections.singleton(new MockPropertySource()), Option.IGNORE_IMPORTS); ConfigData configData = new ConfigData(Collections.singleton(new MockPropertySource()), Option.IGNORE_IMPORTS);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION, ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION,
resource, false, configData, 0); resource, false, configData, 0, this.conversionService);
ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(Collections.singleton(contributor), ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(Collections.singleton(contributor),
null); null);
assertThat(bound).isNotNull(); assertThat(bound).isNotNull();
@ -372,7 +382,7 @@ class ConfigDataEnvironmentContributorTests {
private ConfigDataEnvironmentContributor createBoundContributor(ConfigDataResource resource, ConfigData configData, private ConfigDataEnvironmentContributor createBoundContributor(ConfigDataResource resource, ConfigData configData,
int propertySourceIndex) { int propertySourceIndex) {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION, ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(TEST_LOCATION,
resource, false, configData, propertySourceIndex); resource, false, configData, propertySourceIndex, this.conversionService);
return contributor.withBoundProperties(Collections.singleton(contributor), null); return contributor.withBoundProperties(Collections.singleton(contributor), null);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2023 the original author or authors. * Copyright 2012-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,6 +37,8 @@ import org.springframework.boot.context.config.ConfigDataEnvironmentContributors
import org.springframework.boot.context.properties.bind.BindException; import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
@ -68,9 +70,7 @@ class ConfigDataEnvironmentContributorsTests {
private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); private final DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
private MockEnvironment environment; private final ConversionService conversionService = DefaultConversionService.getSharedInstance();
private Binder binder;
private ConfigDataImporter importer; private ConfigDataImporter importer;
@ -78,10 +78,10 @@ class ConfigDataEnvironmentContributorsTests {
@BeforeEach @BeforeEach
void setup() { void setup() {
this.environment = new MockEnvironment(); MockEnvironment environment = new MockEnvironment();
this.binder = Binder.get(this.environment); Binder binder = Binder.get(environment);
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
this.binder, new DefaultResourceLoader(getClass().getClassLoader()), binder, new DefaultResourceLoader(getClass().getClassLoader()),
SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader())); SpringFactoriesLoader.forDefaultResourceLocation(getClass().getClassLoader()));
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
SpringFactoriesLoader.forDefaultResourceLocation()); SpringFactoriesLoader.forDefaultResourceLocation());
@ -91,9 +91,10 @@ class ConfigDataEnvironmentContributorsTests {
@Test @Test
void createCreatesWithInitialContributors() { void createCreatesWithInitialContributors() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
Iterator<ConfigDataEnvironmentContributor> iterator = contributors.iterator(); Iterator<ConfigDataEnvironmentContributor> iterator = contributors.iterator();
assertThat(iterator.next()).isSameAs(contributor); assertThat(iterator.next()).isSameAs(contributor);
assertThat(iterator.next().getKind()).isEqualTo(Kind.ROOT); assertThat(iterator.next().getKind()).isEqualTo(Kind.ROOT);
@ -102,9 +103,9 @@ class ConfigDataEnvironmentContributorsTests {
@Test @Test
void withProcessedImportsWhenHasNoUnprocessedImportsReturnsSameInstance() { void withProcessedImportsWhenHasNoUnprocessedImportsReturnsSameInstance() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
.ofExisting(new MockPropertySource()); .ofExisting(new MockPropertySource(), this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer, ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
this.activationContext); this.activationContext);
assertThat(withProcessedImports).isSameAs(contributors); assertThat(withProcessedImports).isSameAs(contributors);
@ -113,16 +114,17 @@ class ConfigDataEnvironmentContributorsTests {
@Test @Test
void withProcessedImportsResolvesAndLoads() { void withProcessedImportsResolvesAndLoads() {
this.importer = mock(ConfigDataImporter.class); this.importer = mock(ConfigDataImporter.class);
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1); List<ConfigDataLocation> locations = Collections.singletonList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>();
imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a"), false), imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a"), false),
new ConfigData(Arrays.asList(propertySource))); new ConfigData(List.of(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported); .willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer, ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
this.activationContext); this.activationContext);
Iterator<ConfigDataEnvironmentContributor> iterator = withProcessedImports.iterator(); Iterator<ConfigDataEnvironmentContributor> iterator = withProcessedImports.iterator();
@ -135,24 +137,25 @@ class ConfigDataEnvironmentContributorsTests {
@Test @Test
void withProcessedImportsResolvesAndLoadsChainedImports() { void withProcessedImportsResolvesAndLoadsChainedImports() {
this.importer = mock(ConfigDataImporter.class); this.importer = mock(ConfigDataImporter.class);
List<ConfigDataLocation> initialLocations = Arrays.asList(LOCATION_1); List<ConfigDataLocation> initialLocations = Collections.singletonList(LOCATION_1);
MockPropertySource initialPropertySource = new MockPropertySource(); MockPropertySource initialPropertySource = new MockPropertySource();
initialPropertySource.setProperty("spring.config.import", "location2"); initialPropertySource.setProperty("spring.config.import", "location2");
Map<ConfigDataResolutionResult, ConfigData> initialImported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> initialImported = new LinkedHashMap<>();
initialImported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a"), false), initialImported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a"), false),
new ConfigData(Arrays.asList(initialPropertySource))); new ConfigData(List.of(initialPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations)))
.willReturn(initialImported); .willReturn(initialImported);
List<ConfigDataLocation> secondLocations = Arrays.asList(LOCATION_2); List<ConfigDataLocation> secondLocations = Collections.singletonList(LOCATION_2);
MockPropertySource secondPropertySource = new MockPropertySource(); MockPropertySource secondPropertySource = new MockPropertySource();
Map<ConfigDataResolutionResult, ConfigData> secondImported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> secondImported = new LinkedHashMap<>();
secondImported.put(new ConfigDataResolutionResult(LOCATION_2, new TestConfigDataResource("b"), false), secondImported.put(new ConfigDataResolutionResult(LOCATION_2, new TestConfigDataResource("b"), false),
new ConfigData(Arrays.asList(secondPropertySource))); new ConfigData(List.of(secondPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations)))
.willReturn(secondImported); .willReturn(secondImported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer, ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
this.activationContext); this.activationContext);
Iterator<ConfigDataEnvironmentContributor> iterator = withProcessedImports.iterator(); Iterator<ConfigDataEnvironmentContributor> iterator = withProcessedImports.iterator();
@ -168,18 +171,19 @@ class ConfigDataEnvironmentContributorsTests {
MockPropertySource existingPropertySource = new MockPropertySource(); MockPropertySource existingPropertySource = new MockPropertySource();
existingPropertySource.setProperty("test", "springboot"); existingPropertySource.setProperty("test", "springboot");
ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor
.ofExisting(existingPropertySource); .ofExisting(existingPropertySource, this.conversionService);
this.importer = mock(ConfigDataImporter.class); this.importer = mock(ConfigDataImporter.class);
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1); List<ConfigDataLocation> locations = Collections.singletonList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>();
imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a'"), false), imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a'"), false),
new ConfigData(Arrays.asList(propertySource))); new ConfigData(List.of(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported); .willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(existingContributor, contributor)); this.bootstrapContext, Arrays.asList(existingContributor, contributor), this.conversionService);
contributors.withProcessedImports(this.importer, this.activationContext); contributors.withProcessedImports(this.importer, this.activationContext);
then(this.importer).should() then(this.importer).should()
.resolveAndLoad(any(), .resolveAndLoad(any(),
@ -191,24 +195,25 @@ class ConfigDataEnvironmentContributorsTests {
@Test @Test
void withProcessedImportsProvidesLocationResolverContextWithAccessToParent() { void withProcessedImportsProvidesLocationResolverContextWithAccessToParent() {
this.importer = mock(ConfigDataImporter.class); this.importer = mock(ConfigDataImporter.class);
List<ConfigDataLocation> initialLocations = Arrays.asList(LOCATION_1); List<ConfigDataLocation> initialLocations = Collections.singletonList(LOCATION_1);
MockPropertySource initialPropertySource = new MockPropertySource(); MockPropertySource initialPropertySource = new MockPropertySource();
initialPropertySource.setProperty("spring.config.import", "location2"); initialPropertySource.setProperty("spring.config.import", "location2");
Map<ConfigDataResolutionResult, ConfigData> initialImported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> initialImported = new LinkedHashMap<>();
initialImported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a"), false), initialImported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a"), false),
new ConfigData(Arrays.asList(initialPropertySource))); new ConfigData(List.of(initialPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations)))
.willReturn(initialImported); .willReturn(initialImported);
List<ConfigDataLocation> secondLocations = Arrays.asList(LOCATION_2); List<ConfigDataLocation> secondLocations = Collections.singletonList(LOCATION_2);
MockPropertySource secondPropertySource = new MockPropertySource(); MockPropertySource secondPropertySource = new MockPropertySource();
Map<ConfigDataResolutionResult, ConfigData> secondImported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> secondImported = new LinkedHashMap<>();
secondImported.put(new ConfigDataResolutionResult(LOCATION_2, new TestConfigDataResource("b"), false), secondImported.put(new ConfigDataResolutionResult(LOCATION_2, new TestConfigDataResource("b"), false),
new ConfigData(Arrays.asList(secondPropertySource))); new ConfigData(List.of(secondPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations)))
.willReturn(secondImported); .willReturn(secondImported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
ArgumentCaptor<ConfigDataLocationResolverContext> locationResolverContext = ArgumentCaptor ArgumentCaptor<ConfigDataLocationResolverContext> locationResolverContext = ArgumentCaptor
.forClass(ConfigDataLocationResolverContext.class); .forClass(ConfigDataLocationResolverContext.class);
contributors.withProcessedImports(this.importer, this.activationContext); contributors.withProcessedImports(this.importer, this.activationContext);
@ -223,18 +228,19 @@ class ConfigDataEnvironmentContributorsTests {
MockPropertySource existingPropertySource = new MockPropertySource(); MockPropertySource existingPropertySource = new MockPropertySource();
existingPropertySource.setProperty("test", "springboot"); existingPropertySource.setProperty("test", "springboot");
ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor
.ofExisting(existingPropertySource); .ofExisting(existingPropertySource, this.conversionService);
this.importer = mock(ConfigDataImporter.class); this.importer = mock(ConfigDataImporter.class);
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1); List<ConfigDataLocation> locations = Collections.singletonList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>();
imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a'"), false), imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a'"), false),
new ConfigData(Arrays.asList(propertySource))); new ConfigData(List.of(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported); .willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(existingContributor, contributor)); this.bootstrapContext, Arrays.asList(existingContributor, contributor), this.conversionService);
contributors.withProcessedImports(this.importer, this.activationContext); contributors.withProcessedImports(this.importer, this.activationContext);
then(this.importer).should() then(this.importer).should()
.resolveAndLoad(any(), .resolveAndLoad(any(),
@ -247,18 +253,19 @@ class ConfigDataEnvironmentContributorsTests {
MockPropertySource existingPropertySource = new MockPropertySource(); MockPropertySource existingPropertySource = new MockPropertySource();
existingPropertySource.setProperty("test", "springboot"); existingPropertySource.setProperty("test", "springboot");
ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor
.ofExisting(existingPropertySource); .ofExisting(existingPropertySource, this.conversionService);
this.importer = mock(ConfigDataImporter.class); this.importer = mock(ConfigDataImporter.class);
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1); List<ConfigDataLocation> locations = Collections.singletonList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>(); Map<ConfigDataResolutionResult, ConfigData> imported = new LinkedHashMap<>();
imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a'"), false), imported.put(new ConfigDataResolutionResult(LOCATION_1, new TestConfigDataResource("a'"), false),
new ConfigData(Arrays.asList(propertySource))); new ConfigData(List.of(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported); .willReturn(imported);
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(existingContributor, contributor)); this.bootstrapContext, Arrays.asList(existingContributor, contributor), this.conversionService);
contributors.withProcessedImports(this.importer, this.activationContext); contributors.withProcessedImports(this.importer, this.activationContext);
then(this.importer).should() then(this.importer).should()
.resolveAndLoad(any(), any(), .resolveAndLoad(any(), any(),
@ -270,9 +277,10 @@ class ConfigDataEnvironmentContributorsTests {
void getBinderProvidesBinder() { void getBinderProvidesBinder() {
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("test", "springboot"); propertySource.setProperty("test", "springboot");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext); Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot"); assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot");
} }
@ -287,7 +295,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0); ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1); ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor)); this.bootstrapContext, Arrays.asList(firstContributor, secondContributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext); Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("one"); assertThat(binder.bind("test", String.class).get()).isEqualTo("one");
} }
@ -303,7 +311,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0); ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1); ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor)); this.bootstrapContext, Arrays.asList(firstContributor, secondContributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext); Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("two"); assertThat(binder.bind("test", String.class).get()).isEqualTo("two");
} }
@ -313,9 +321,10 @@ class ConfigDataEnvironmentContributorsTests {
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("test", "${other}"); propertySource.setProperty("test", "${other}");
propertySource.setProperty("other", "springboot"); propertySource.setProperty("other", "springboot");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource,
this.conversionService);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor)); this.bootstrapContext, List.of(contributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext); Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot"); assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot");
} }
@ -332,7 +341,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0); ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1); ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor)); this.bootstrapContext, Arrays.asList(firstContributor, secondContributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext); Binder binder = contributors.getBinder(this.activationContext);
assertThat(binder.bind("test", String.class).get()).isEqualTo("two"); assertThat(binder.bind("test", String.class).get()).isEqualTo("two");
} }
@ -348,7 +357,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0); ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1); ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor)); this.bootstrapContext, Arrays.asList(firstContributor, secondContributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class)) assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class)); .satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
@ -365,7 +374,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0); ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1); ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor)); this.bootstrapContext, Arrays.asList(firstContributor, secondContributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class)) assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class)); .satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
@ -383,7 +392,7 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0); ConfigDataEnvironmentContributor firstContributor = createBoundImportContributor(configData, 0);
ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1); ConfigDataEnvironmentContributor secondContributor = createBoundImportContributor(configData, 1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(firstContributor, secondContributor)); this.bootstrapContext, Arrays.asList(firstContributor, secondContributor), this.conversionService);
Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class)) assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class))
.satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class)); .satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class));
@ -392,7 +401,7 @@ class ConfigDataEnvironmentContributorsTests {
private ConfigDataEnvironmentContributor createBoundImportContributor(ConfigData configData, private ConfigDataEnvironmentContributor createBoundImportContributor(ConfigData configData,
int propertySourceIndex) { int propertySourceIndex) {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(null, null, ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(null, null,
false, configData, propertySourceIndex); false, configData, propertySourceIndex, this.conversionService);
return contributor.withBoundProperties(Collections.singleton(contributor), null); return contributor.withBoundProperties(Collections.singleton(contributor), null);
} }

View File

@ -42,6 +42,8 @@ import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.
import org.springframework.boot.context.config.TestConfigDataEnvironmentUpdateListener.AddedPropertySource; import org.springframework.boot.context.config.TestConfigDataEnvironmentUpdateListener.AddedPropertySource;
import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
@ -73,6 +75,8 @@ class ConfigDataEnvironmentTests {
private final Collection<String> additionalProfiles = Collections.emptyList(); private final Collection<String> additionalProfiles = Collections.emptyList();
private final ConversionService conversionService = DefaultConversionService.getSharedInstance();
@Test @Test
void createExposesEnvironmentBinderToConfigDataLocationResolvers() { void createExposesEnvironmentBinderToConfigDataLocationResolvers() {
this.environment.setProperty("spring", "boot"); this.environment.setProperty("spring", "boot");
@ -217,7 +221,8 @@ class ConfigDataEnvironmentTests {
ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)), ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)),
ConfigData.Option.IGNORE_PROFILES); ConfigData.Option.IGNORE_PROFILES);
contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"), contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"),
mock(ConfigDataResource.class), false, data, 0)); mock(ConfigDataResource.class), false, data, 0,
ConfigDataEnvironmentTests.this.conversionService));
return super.createContributors(contributors); return super.createContributors(contributors);
} }
@ -241,7 +246,8 @@ class ConfigDataEnvironmentTests {
source.put("spring.profiles." + property, "include"); source.put("spring.profiles." + property, "include");
ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source))); ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)));
contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"), contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"),
mock(ConfigDataResource.class), false, data, 0)); mock(ConfigDataResource.class), false, data, 0,
ConfigDataEnvironmentTests.this.conversionService));
return super.createContributors(contributors); return super.createContributors(contributors);
} }
@ -264,7 +270,8 @@ class ConfigDataEnvironmentTests {
source.put(property, "included"); source.put(property, "included");
ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source))); ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)));
contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"), contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"),
mock(ConfigDataResource.class), false, data, 0)); mock(ConfigDataResource.class), false, data, 0,
ConfigDataEnvironmentTests.this.conversionService));
return super.createContributors(contributors); return super.createContributors(contributors);
} }
@ -288,7 +295,8 @@ class ConfigDataEnvironmentTests {
ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)), ConfigData data = new ConfigData(Collections.singleton(new MapPropertySource("test", source)),
ConfigData.Option.IGNORE_PROFILES); ConfigData.Option.IGNORE_PROFILES);
contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"), contributors.add(ConfigDataEnvironmentContributor.ofUnboundImport(ConfigDataLocation.of("test"),
mock(ConfigDataResource.class), false, data, 0)); mock(ConfigDataResource.class), false, data, 0,
ConfigDataEnvironmentTests.this.conversionService));
return super.createContributors(contributors); return super.createContributors(contributors);
} }
@ -305,7 +313,7 @@ class ConfigDataEnvironmentTests {
ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapContext,
this.environment, this.resourceLoader, this.additionalProfiles, null); this.environment, this.resourceLoader, this.additionalProfiles, null);
assertThatExceptionOfType(InvalidConfigDataPropertyException.class) assertThatExceptionOfType(InvalidConfigDataPropertyException.class)
.isThrownBy(() -> configDataEnvironment.processAndApply()); .isThrownBy(configDataEnvironment::processAndApply);
} }
@Test @Test

View File

@ -23,6 +23,8 @@ import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.origin.MockOrigin; import org.springframework.boot.origin.MockOrigin;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.mock.env.MockPropertySource; import org.springframework.mock.env.MockPropertySource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -46,6 +48,8 @@ class InvalidConfigDataPropertyExceptionTests {
private final ConfigurationProperty property = new ConfigurationProperty(this.invalid, "bad", private final ConfigurationProperty property = new ConfigurationProperty(this.invalid, "bad",
MockOrigin.of("origin")); MockOrigin.of("origin"));
private final ConversionService conversionService = DefaultConversionService.getSharedInstance();
@Test @Test
void createHasCorrectMessage() { void createHasCorrectMessage() {
assertThat(new InvalidConfigDataPropertyException(this.property, false, this.replacement, this.resource)) assertThat(new InvalidConfigDataPropertyException(this.property, false, this.replacement, this.resource))
@ -104,7 +108,8 @@ class InvalidConfigDataPropertyExceptionTests {
void throwOrWarnWhenHasInvalidPropertyThrowsException() { void throwOrWarnWhenHasInvalidPropertyThrowsException() {
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("spring.profiles", "a"); propertySource.setProperty("spring.profiles", "a");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource,
this.conversionService);
assertThatExceptionOfType(InvalidConfigDataPropertyException.class) assertThatExceptionOfType(InvalidConfigDataPropertyException.class)
.isThrownBy(() -> InvalidConfigDataPropertyException.throwIfPropertyFound(contributor)) .isThrownBy(() -> InvalidConfigDataPropertyException.throwIfPropertyFound(contributor))
.withMessageStartingWith("Property 'spring.profiles' is invalid and should be replaced with " .withMessageStartingWith("Property 'spring.profiles' is invalid and should be replaced with "
@ -136,16 +141,15 @@ class InvalidConfigDataPropertyExceptionTests {
ConfigData.Option... configDataOptions) { ConfigData.Option... configDataOptions) {
MockPropertySource propertySource = new MockPropertySource(); MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty(name, "a"); propertySource.setProperty(name, "a");
ConfigDataEnvironmentContributor contributor = new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, null, return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, null, null, true, propertySource,
null, true, propertySource, ConfigurationPropertySource.from(propertySource), null, ConfigurationPropertySource.from(propertySource), null, ConfigData.Options.of(configDataOptions), null,
ConfigData.Options.of(configDataOptions), null); this.conversionService);
return contributor;
} }
@Test @Test
void throwOrWarnWhenHasNoInvalidPropertyDoesNothing() { void throwOrWarnWhenHasNoInvalidPropertyDoesNothing() {
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
.ofExisting(new MockPropertySource()); .ofExisting(new MockPropertySource(), this.conversionService);
InvalidConfigDataPropertyException.throwIfPropertyFound(contributor); InvalidConfigDataPropertyException.throwIfPropertyFound(contributor);
} }