From 4fc0dec6c4093bac4a488c87461021be57549b87 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 2 Jun 2020 20:36:50 -0700 Subject: [PATCH] Support wrapped random property sources Update `SpringConfigurationPropertySource` so that wrapped random property sources can be used. It's assumed that wrapped random sources will use the name of the source as the prefix. Closes gh-21595 --- .../SpringConfigurationPropertySource.java | 15 ++- .../boot/env/RandomValuePropertySource.java | 8 +- ...pringConfigurationPropertySourceTests.java | 93 +++++++++++++++++-- 3 files changed, 101 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java index 7074b83f17..8503747b0b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Random; import java.util.function.Function; +import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form; import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.PropertySourceOrigin; import org.springframework.core.env.EnumerablePropertySource; @@ -53,8 +54,6 @@ import org.springframework.util.ObjectUtils; */ class SpringConfigurationPropertySource implements ConfigurationPropertySource { - private static final ConfigurationPropertyName RANDOM = ConfigurationPropertyName.of("random"); - private final PropertySource propertySource; private final PropertyMapper mapper; @@ -184,13 +183,19 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource { private static Function getContainsDescendantOfForSource( PropertySource source) { if (source.getSource() instanceof Random) { - return SpringConfigurationPropertySource::containsDescendantOfForRandom; + return (name) -> containsDescendantOfForRandom("random", name); + } + if (source.getSource() instanceof PropertySource + && ((PropertySource) source.getSource()).getSource() instanceof Random) { + // Assume wrapped random sources use the source name as the prefix + return (name) -> containsDescendantOfForRandom(source.getName(), name); } return null; } - private static ConfigurationPropertyState containsDescendantOfForRandom(ConfigurationPropertyName name) { - if (RANDOM.isAncestorOf(name) || name.equals(RANDOM)) { + private static ConfigurationPropertyState containsDescendantOfForRandom(String prefix, + ConfigurationPropertyName name) { + if (name.getNumberOfElements() > 1 && name.getElement(0, Form.DASHED).equals(prefix)) { return ConfigurationPropertyState.PRESENT; } return ConfigurationPropertyState.ABSENT; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySource.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySource.java index d210feded5..7c441a671b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySource.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySource.java @@ -62,14 +62,14 @@ public class RandomValuePropertySource extends PropertySource { private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class); - public RandomValuePropertySource(String name) { - super(name, new Random()); - } - public RandomValuePropertySource() { this(RANDOM_PROPERTY_SOURCE_NAME); } + public RandomValuePropertySource(String name) { + super(name, new Random()); + } + @Override public Object getProperty(String name) { if (!name.startsWith(PREFIX)) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java index 2caf833733..ccc75de74a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySourceTests.java @@ -153,24 +153,105 @@ class SpringConfigurationPropertySourceTests { void containsDescendantOfWhenRandomSourceAndRandomPropertyReturnsPresent() { SpringConfigurationPropertySource source = SpringConfigurationPropertySource .from(new RandomValuePropertySource()); - assertThat(source.containsDescendantOf(ConfigurationPropertyName.of("random"))) - .isEqualTo(ConfigurationPropertyState.PRESENT); + ConfigurationPropertyName name = ConfigurationPropertyName.of("random"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.ABSENT); + assertThat(source.getConfigurationProperty(name)).isNull(); } @Test void containsDescendantOfWhenRandomSourceAndRandomPrefixedPropertyReturnsPresent() { SpringConfigurationPropertySource source = SpringConfigurationPropertySource .from(new RandomValuePropertySource()); - assertThat(source.containsDescendantOf(ConfigurationPropertyName.of("random.something"))) - .isEqualTo(ConfigurationPropertyState.PRESENT); + ConfigurationPropertyName name = ConfigurationPropertyName.of("random.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.PRESENT); + assertThat(source.getConfigurationProperty(name)).isNotNull(); + } + + @Test + void containsDescendantOfWhenRandomSourceWithDifferentNameAndRandomPrefixedPropertyReturnsPresent() { + SpringConfigurationPropertySource source = SpringConfigurationPropertySource + .from(new RandomValuePropertySource("different")); + ConfigurationPropertyName name = ConfigurationPropertyName.of("random.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.PRESENT); + assertThat(source.getConfigurationProperty(name)).isNotNull(); } @Test void containsDescendantOfWhenRandomSourceAndNonRandomPropertyReturnsAbsent() { SpringConfigurationPropertySource source = SpringConfigurationPropertySource .from(new RandomValuePropertySource()); - assertThat(source.containsDescendantOf(ConfigurationPropertyName.of("abandon.something"))) - .isEqualTo(ConfigurationPropertyState.ABSENT); + ConfigurationPropertyName name = ConfigurationPropertyName.of("abandon.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.ABSENT); + assertThat(source.getConfigurationProperty(name)).isNull(); + } + + @Test + void containsDescendantOfWhenWrappedRandomSourceAndRandomPropertyReturnsPresent() { + SpringConfigurationPropertySource source = SpringConfigurationPropertySource + .from(new RandomWrapperPropertySource()); + ConfigurationPropertyName name = ConfigurationPropertyName.of("cachedrandom"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.ABSENT); + assertThat(source.getConfigurationProperty(name)).isNull(); + } + + @Test + void containsDescendantOfWhenWrappedRandomSourceAndRandomPrefixedPropertyReturnsPresent() { + SpringConfigurationPropertySource source = SpringConfigurationPropertySource + .from(new RandomWrapperPropertySource()); + ConfigurationPropertyName name = ConfigurationPropertyName.of("cachedrandom.something.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.ABSENT); + assertThat(source.getConfigurationProperty(name)).isNull(); + } + + @Test + void containsDescendantOfWhenWrappedRandomSourceWithMatchingNameAndRandomPrefixedPropertyReturnsPresent() { + SpringConfigurationPropertySource source = SpringConfigurationPropertySource + .from(new RandomWrapperPropertySource("cachedrandom")); + ConfigurationPropertyName name = ConfigurationPropertyName.of("cachedrandom.something.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.PRESENT); + assertThat(source.getConfigurationProperty(name)).isNotNull(); + } + + @Test + void containsDescendantOfWhenWrappedRandomSourceAndRandomDashPrefixedPropertyReturnsPresent() { + SpringConfigurationPropertySource source = SpringConfigurationPropertySource + .from(new RandomWrapperPropertySource()); + ConfigurationPropertyName name = ConfigurationPropertyName.of("cached-random.something.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.ABSENT); + assertThat(source.getConfigurationProperty(name)).isNull(); + } + + @Test + void containsDescendantOfWhenWrappedRandomSourceAndNonRandomPropertyReturnsAbsent() { + SpringConfigurationPropertySource source = SpringConfigurationPropertySource + .from(new RandomWrapperPropertySource()); + ConfigurationPropertyName name = ConfigurationPropertyName.of("abandon.something.int"); + assertThat(source.containsDescendantOf(name)).isEqualTo(ConfigurationPropertyState.ABSENT); + assertThat(source.getConfigurationProperty(name)).isNull(); + } + + static class RandomWrapperPropertySource extends PropertySource { + + private final String prefix; + + RandomWrapperPropertySource() { + this("cachedRandom"); + } + + RandomWrapperPropertySource(String name) { + super(name, new RandomValuePropertySource()); + this.prefix = name + "."; + } + + @Override + public Object getProperty(String name) { + name = name.toLowerCase(); + if (!name.startsWith(this.prefix)) { + return null; + } + return getSource().getProperty("random." + name.substring(this.prefix.length())); + } + } /**