Skip to content

Commit

Permalink
Use ConversionService when resolving placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
mhalbritter committed Mar 27, 2024
1 parent 3ab21ce commit c2d4995
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,6 +36,7 @@
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
Expand Down Expand Up @@ -288,7 +289,7 @@ private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributo
private Collection<? extends String> getIncludedProfiles(ConfigDataEnvironmentContributors contributors,
ConfigDataActivationContext activationContext) {
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, activationContext, null, true);
contributors, activationContext, null, true, DefaultConversionService.getSharedInstance());
Set<String> result = new LinkedHashSet<>();
for (ConfigDataEnvironmentContributor contributor : contributors) {
ConfigurationPropertySource source = contributor.getConfigurationPropertySource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;
Expand Down Expand Up @@ -235,7 +236,7 @@ ConfigDataEnvironmentContributor withBoundProperties(Iterable<ConfigDataEnvironm
ConfigDataActivationContext activationContext) {
Iterable<ConfigurationPropertySource> sources = Collections.singleton(getConfigurationPropertySource());
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, activationContext, this, true);
contributors, activationContext, this, true, DefaultConversionService.getSharedInstance());
Binder binder = new Binder(sources, placeholdersResolver, null, null, null);
ConfigDataProperties properties = ConfigDataProperties.get(binder);
if (properties != null && this.configDataOptions.contains(ConfigData.Option.IGNORE_IMPORTS)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand All @@ -20,6 +20,7 @@
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.PropertySource;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.SystemPropertyUtils;
Expand All @@ -30,6 +31,7 @@
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Moritz Halbritter
*/
class ConfigDataEnvironmentContributorPlaceholdersResolver implements PlaceholdersResolver {

Expand All @@ -43,13 +45,16 @@ class ConfigDataEnvironmentContributorPlaceholdersResolver implements Placeholde

private final ConfigDataEnvironmentContributor activeContributor;

private final ConversionService conversionService;

ConfigDataEnvironmentContributorPlaceholdersResolver(Iterable<ConfigDataEnvironmentContributor> contributors,
ConfigDataActivationContext activationContext, ConfigDataEnvironmentContributor activeContributor,
boolean failOnResolveFromInactiveContributor) {
boolean failOnResolveFromInactiveContributor, ConversionService conversionService) {
this.contributors = contributors;
this.activationContext = activationContext;
this.activeContributor = activeContributor;
this.failOnResolveFromInactiveContributor = failOnResolveFromInactiveContributor;
this.conversionService = conversionService;
this.helper = new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
}
Expand Down Expand Up @@ -77,7 +82,7 @@ private String resolvePlaceholder(String placeholder) {
}
result = (result != null) ? result : value;
}
return (result != null) ? String.valueOf(result) : null;
return (result != null) ? convertValueIfNecessary(result) : null;
}

private boolean isActive(ConfigDataEnvironmentContributor contributor) {
Expand All @@ -91,4 +96,11 @@ private boolean isActive(ConfigDataEnvironmentContributor contributor) {
.isActive(this.activationContext);
}

private String convertValueIfNecessary(Object value) {
if (value instanceof String string) {
return string;
}
return this.conversionService.convert(value, String.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.log.LogMessage;
import org.springframework.util.ObjectUtils;

Expand Down Expand Up @@ -214,7 +215,7 @@ private Binder getBinder(ConfigDataActivationContext activationContext,
Iterable<ConfigurationPropertySource> sources = () -> getBinderSources(
filter.and((contributor) -> failOnInactiveSource || contributor.isActive(activationContext)));
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(this.root,
activationContext, null, failOnInactiveSource);
activationContext, null, failOnInactiveSource, DefaultConversionService.getSharedInstance());
BindHandler bindHandler = !failOnInactiveSource ? null : new InactiveSourceChecker(activationContext);
return new Binder(sources, placeholdersResolver, null, null, bindHandler);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,6 +27,8 @@
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.origin.PropertySourceOrigin;
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.PropertySource;

Expand All @@ -38,20 +40,21 @@
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Moritz Halbritter
*/
class ConfigDataEnvironmentContributorPlaceholdersResolverTests {

@Test
void resolvePlaceholdersWhenNotStringReturnsResolved() {
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
Collections.emptyList(), null, null, false);
Collections.emptyList(), null, null, false, DefaultConversionService.getSharedInstance());
assertThat(resolver.resolvePlaceholders(123)).isEqualTo(123);
}

@Test
void resolvePlaceholdersWhenNotFoundReturnsOriginal() {
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
Collections.emptyList(), null, null, false);
Collections.emptyList(), null, null, false, DefaultConversionService.getSharedInstance());
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("${test}");
}

Expand All @@ -62,18 +65,30 @@ void resolvePlaceholdersWhenFoundReturnsFirstMatch() {
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), true));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, true);
contributors, null, null, true, DefaultConversionService.getSharedInstance());
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("t2");
}

@Test
void shouldUseConversionService() {
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>();
contributors.add(new TestConfigDataEnvironmentContributor(
new TestPropertySource("s1", Map.of("test", new CustomValue())), true));
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(CustomValue.class, String.class, (input) -> "custom-value");
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, true, conversionService);
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("custom-value");
}

@Test
void resolvePlaceholdersWhenFoundInInactiveThrowsException() {
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>();
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s1", "nope", "t1"), true));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), false));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, true);
contributors, null, null, true, DefaultConversionService.getSharedInstance());
assertThatExceptionOfType(InactiveConfigDataAccessException.class)
.isThrownBy(() -> resolver.resolvePlaceholders("${test}"))
.satisfies(propertyNameAndOriginOf("test", "s3"));
Expand All @@ -86,7 +101,7 @@ void resolvePlaceholderWhenFoundInInactiveAndIgnoringReturnsResolved() {
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s2", "test", "t2"), true));
contributors.add(new TestConfigDataEnvironmentContributor(new TestPropertySource("s3", "test", "t3"), false));
ConfigDataEnvironmentContributorPlaceholdersResolver resolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
contributors, null, null, false);
contributors, null, null, false, DefaultConversionService.getSharedInstance());
assertThat(resolver.resolvePlaceholders("${test}")).isEqualTo("t2");
}

Expand Down Expand Up @@ -133,4 +148,8 @@ boolean isActive(ConfigDataActivationContext activationContext) {

}

static class CustomValue {

}

}

0 comments on commit c2d4995

Please sign in to comment.