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 1512a3618d6..3999b5cca91 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 @@ -19,6 +19,7 @@ package org.springframework.boot.context.properties.source; import java.util.Map; import java.util.Random; +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; @@ -51,8 +52,6 @@ import org.springframework.util.Assert; */ class SpringConfigurationPropertySource implements ConfigurationPropertySource { - private static final ConfigurationPropertyName RANDOM = ConfigurationPropertyName.of("random"); - private static final PropertyMapper[] DEFAULT_MAPPERS = { DefaultPropertyMapper.INSTANCE }; private static final PropertyMapper[] SYSTEM_ENVIRONMENT_MAPPERS = { SystemEnvironmentPropertyMapper.INSTANCE, @@ -97,14 +96,21 @@ class SpringConfigurationPropertySource implements ConfigurationPropertySource { @Override public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) { - if (getPropertySource().getSource() instanceof Random) { - return containsDescendantOfForRandom(name); + PropertySource source = getPropertySource(); + if (source.getSource() instanceof Random) { + return 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 containsDescendantOfForRandom(source.getName(), name); } return ConfigurationPropertyState.UNKNOWN; } - 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 d210feded5f..7c441a671b1 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 f5aeee6c6ad..605de0eab40 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 @@ -146,24 +146,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())); + } + } /**