Rework ConfigurationPropertySources

Rework the ConfigurationPropertySources and related adapter classes to
help with performance. The ConfigurationPropertySources class now only
monitors for updates when `.attach` is used. The `.get` methods now
return the adapted version, but no longer checks to see if sources have
been added or removed on each call.

This commit also fixes a few caching issues and makes both the
`PropertyMapper` implementations true static singletons.

See gh-9000
This commit is contained in:
Phillip Webb 2017-05-09 20:53:47 -07:00
parent 133f11df2f
commit fa4de13519
25 changed files with 646 additions and 443 deletions

View File

@ -25,7 +25,6 @@ import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.info.InfoProperties;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
@ -93,9 +92,7 @@ public abstract class InfoPropertiesInfoContributor<T extends InfoProperties>
* @return the raw content
*/
protected Map<String, Object> extractContent(PropertySource<?> propertySource) {
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(propertySource);
return new Binder(ConfigurationPropertySources.get(sources))
return new Binder(ConfigurationPropertySources.from(propertySource))
.bind("", STRING_OBJECT_MAP).orElseGet(LinkedHashMap::new);
}

View File

@ -21,6 +21,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
@ -320,9 +321,8 @@ public class EndpointAutoConfigurationTests {
if (!location.exists()) {
return;
}
MapConfigurationPropertySource source = new MapConfigurationPropertySource(
PropertiesLoaderUtils.loadProperties(location));
new Binder(source).bind("info",
Properties properties = PropertiesLoaderUtils.loadProperties(location);
new Binder(new MapConfigurationPropertySource(properties)).bind("info",
Bindable.of(STRING_OBJECT_MAP).withExistingValue(this.content));
}

View File

@ -28,6 +28,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.boot.test.mock.web.SpringBootMockServletContext;
import org.springframework.boot.test.util.EnvironmentTestUtils;
@ -173,7 +174,7 @@ public class SpringBootContextLoader extends AbstractContextLoader {
return binder.bind("server.port", Bindable.of(String.class)).isBound();
}
private MapConfigurationPropertySource convertToConfigurationPropertySource(
private ConfigurationPropertySource convertToConfigurationPropertySource(
List<String> properties) {
String[] array = properties.toArray(new String[properties.size()]);
return new MapConfigurationPropertySource(

View File

@ -480,9 +480,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
private void handleProfileProperties(PropertySource<?> propertySource) {
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(propertySource);
Binder binder = new Binder(ConfigurationPropertySources.get(propertySources),
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
new PropertySourcesPlaceholdersResolver(this.environment));
Set<Profile> active = getProfiles(binder, "spring.profiles.active");
Set<Profile> include = getProfiles(binder, "spring.profiles.include");

View File

@ -40,6 +40,7 @@ import org.springframework.boot.context.properties.bind.PropertySourcesPlacehold
import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler;
import org.springframework.boot.context.properties.bind.handler.NoUnboundElementsBindHandler;
import org.springframework.boot.context.properties.bind.validation.ValidationBindHandler;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.ApplicationContext;
@ -59,6 +60,7 @@ import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.Assert;
@ -95,7 +97,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
private ConfigurationBeanFactoryMetaData beans = new ConfigurationBeanFactoryMetaData();
private PropertySources propertySources;
private Iterable<PropertySource<?>> propertySources;
private Validator validator;
@ -117,7 +119,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
private int order = Ordered.HIGHEST_PRECEDENCE + 1;
private ConfigurationPropertySources configurationSources;
private Iterable<ConfigurationPropertySource> configurationSources;
private Binder binder;
@ -164,7 +166,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
* Set the property sources to bind.
* @param propertySources the property sources
*/
public void setPropertySources(PropertySources propertySources) {
public void setPropertySources(Iterable<PropertySource<?>> propertySources) {
this.propertySources = propertySources;
}
@ -221,7 +223,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
ConversionService.class);
}
this.configurationSources = ConfigurationPropertySources
.get(this.propertySources);
.from(this.propertySources);
}
@Override

View File

@ -37,7 +37,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyN
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.Assert;
@ -331,13 +330,12 @@ public class Binder {
/**
* Create a new {@link Binder} instance from the specified environment.
* @param environment the environment (must be a {@link ConfigurableEnvironment})
* @param environment the environment source (must have attached
* {@link ConfigurationPropertySources})
* @return a {@link Binder} instance
*/
public static Binder get(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
return new Binder(
ConfigurationPropertySources.get((ConfigurableEnvironment) environment),
return new Binder(ConfigurationPropertySources.get(environment),
new PropertySourcesPlaceholdersResolver(environment));
}

View File

@ -33,7 +33,7 @@ import org.springframework.util.SystemPropertyUtils;
*/
public class PropertySourcesPlaceholdersResolver implements PlaceholdersResolver {
private final PropertySources sources;
private final Iterable<PropertySource<?>> sources;
private final PropertyPlaceholderHelper helper;
@ -41,11 +41,11 @@ public class PropertySourcesPlaceholdersResolver implements PlaceholdersResolver
this(getSources(environment), null);
}
public PropertySourcesPlaceholdersResolver(PropertySources sources) {
public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources) {
this(sources, null);
}
public PropertySourcesPlaceholdersResolver(PropertySources sources,
public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources,
PropertyPlaceholderHelper helper) {
this.sources = sources;
this.helper = (helper != null ? helper

View File

@ -16,72 +16,115 @@
package org.springframework.boot.context.properties.source;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.function.Function;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.boot.env.RandomValuePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySource.StubPropertySource;
import org.springframework.core.env.PropertySources;
import org.springframework.core.env.PropertySourcesPropertyResolver;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import org.springframework.util.Assert;
/**
* A managed set of {@link ConfigurationPropertySource} instances, usually adapted from
* Spring's {@link PropertySources}.
* Provides access to {@link ConfigurationPropertySource ConfigurationPropertySources}.
*
* @author Phillip Webb
* @author Madhura Bhave
* @since 2.0.0
* @see #attach(MutablePropertySources)
* @see #get(PropertySources)
*/
public class ConfigurationPropertySources
implements Iterable<ConfigurationPropertySource> {
private static final ConfigurationPropertyName RANDOM = ConfigurationPropertyName
.of("random");
public final class ConfigurationPropertySources {
/**
* The name of the {@link PropertySource} {@link #adapt adapter}.
*/
public static final String PROPERTY_SOURCE_NAME = "configurationProperties";
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
private final PropertySources propertySources;
private final Map<PropertySource<?>, ConfigurationPropertySource> adapters = new WeakHashMap<>();
private ConfigurationPropertySources() {
}
/**
* Create a new {@link ConfigurationPropertySources} instance.
* @param propertySources the property sources to expose
* Attach a {@link ConfigurationPropertySource} support to the specified
* {@link Environment}. Adapts each {@link PropertySource} managed by the environment
* to a {@link ConfigurationPropertySource} and allows classic
* {@link PropertySourcesPropertyResolver} calls to resolve using
* {@link ConfigurationPropertyName configuration property names}.
* <p>
* The attached resolver will dynamically track any additions or removals from the
* underlying {@link Environment} property sources.
* @param environment the source environment (must be an instance of
* {@link ConfigurableEnvironment})
* @see #get(Environment)
*/
ConfigurationPropertySources(PropertySources propertySources) {
Assert.notNull(propertySources, "PropertySources must not be null");
this.propertySources = propertySources;
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment)
.getPropertySources();
if (!sources.contains(ATTACHED_PROPERTY_SOURCE_NAME)) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(
ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
@Override
public Iterator<ConfigurationPropertySource> iterator() {
return streamPropertySources(this.propertySources)
.filter(s -> !(s instanceof ConfigurationPropertySourcesPropertySource))
.map(this::adapt).iterator();
/**
* Return a set of {@link ConfigurationPropertySource} instances that have previously
* been {@link #attach(Environment) attached} to the {@link Environment}.
* @param environment the source environment (must be an instance of
* {@link ConfigurableEnvironment})
* @return an iterable set of configuration property sources
* @throws IllegalStateException if not configuration property sources have been
* attached
*/
public static Iterable<ConfigurationPropertySource> get(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment)
.getPropertySources();
ConfigurationPropertySourcesPropertySource attached = (ConfigurationPropertySourcesPropertySource) sources
.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached == null) {
return from(sources);
}
return attached.getSource();
}
private Stream<PropertySource<?>> streamPropertySources(PropertySources sources) {
return StreamSupport.stream(sources.spliterator(), false).flatMap(this::flatten)
.filter(this::notStubSource);
/**
* Return {@link Iterable} containing a single new {@link ConfigurationPropertySource}
* adapted from the given Spring {@link PropertySource}.
* @param source the Spring property source to adapt
* @return an {@link Iterable} containing a single newly adapted
* {@link SpringConfigurationPropertySource}
*/
public static Iterable<ConfigurationPropertySource> from(PropertySource<?> source) {
return Collections.singleton(SpringConfigurationPropertySource.from(source));
}
private Stream<PropertySource<?>> flatten(PropertySource<?> source) {
/**
* Return {@link Iterable} containing new {@link ConfigurationPropertySource}
* instances adapted from the given Spring {@link PropertySource PropertySources}.
* <p>
* This method will flatten any nested property sources and will filter all
* {@link StubPropertySource stub property sources}.
* @param sources the Spring property sources to adapt
* @return an {@link Iterable} containing a single newly adapted
* {@link SpringConfigurationPropertySource} instances
*/
public static Iterable<ConfigurationPropertySource> from(
Iterable<PropertySource<?>> sources) {
return streamPropertySources(sources).map(SpringConfigurationPropertySource::from)
.collect(Collectors.toList());
}
private static Stream<PropertySource<?>> streamPropertySources(
Iterable<PropertySource<?>> sources) {
return StreamSupport.stream(sources.spliterator(), false)
.flatMap(ConfigurationPropertySources::flatten)
.filter(ConfigurationPropertySources::isIncluded);
}
private static Stream<PropertySource<?>> flatten(PropertySource<?> source) {
if (source.getSource() instanceof ConfigurableEnvironment) {
return streamPropertySources(
((ConfigurableEnvironment) source.getSource()).getPropertySources());
@ -89,124 +132,9 @@ public class ConfigurationPropertySources
return Stream.of(source);
}
private boolean notStubSource(PropertySource<?> source) {
return !(source instanceof StubPropertySource);
}
private ConfigurationPropertySource adapt(PropertySource<?> source) {
return this.adapters.computeIfAbsent(source, this::createAdapter);
}
private ConfigurationPropertySource createAdapter(PropertySource<?> source) {
PropertyMapper mapper = getPropertyMapper(source);
if (isFullEnumerable(source)) {
return new PropertySourceIterableConfigurationPropertySource(
(EnumerablePropertySource<?>) source, mapper);
}
return new PropertySourceConfigurationPropertySource(source, mapper,
getContainsDescendantOfMethod(source));
}
private PropertyMapper getPropertyMapper(PropertySource<?> source) {
if (source instanceof SystemEnvironmentPropertySource) {
return SystemEnvironmentPropertyMapper.INSTANCE;
}
return DefaultPropertyMapper.INSTANCE;
}
private boolean isFullEnumerable(PropertySource<?> source) {
PropertySource<?> rootSource = getRootSource(source);
if (rootSource.getSource() instanceof Map) {
// Check we're not security restricted
try {
((Map<?, ?>) rootSource.getSource()).size();
}
catch (UnsupportedOperationException ex) {
return false;
}
}
return (source instanceof EnumerablePropertySource);
}
private PropertySource<?> getRootSource(PropertySource<?> source) {
while (source.getSource() != null
&& source.getSource() instanceof PropertySource) {
source = (PropertySource<?>) source.getSource();
}
return source;
}
private Function<ConfigurationPropertyName, Optional<Boolean>> getContainsDescendantOfMethod(
PropertySource<?> source) {
if (source instanceof RandomValuePropertySource) {
return (name) -> Optional
.of(name.isAncestorOf(RANDOM) || name.equals(RANDOM));
}
return null;
}
/**
* Attach a {@link ConfigurationPropertySources} instance to the specified
* {@link ConfigurableEnvironment} so that classic
* {@link PropertySourcesPropertyResolver} calls will resolve using
* {@link ConfigurationPropertyName configuration property names}.
* @param environment the source environment
* @return the instance attached
*/
public static ConfigurationPropertySources attach(
ConfigurableEnvironment environment) {
return attach(environment.getPropertySources());
}
/**
* Attach a {@link ConfigurationPropertySources} instance to the specified
* {@link PropertySources} so that classic {@link PropertySourcesPropertyResolver}
* calls will resolve using using {@link ConfigurationPropertyName configuration
* property names}.
* @param propertySources the source property sources
* @return the instance attached
*/
public static ConfigurationPropertySources attach(
MutablePropertySources propertySources) {
ConfigurationPropertySources adapted = new ConfigurationPropertySources(
propertySources);
propertySources.addFirst(new ConfigurationPropertySourcesPropertySource(
PROPERTY_SOURCE_NAME, adapted));
return adapted;
}
/**
* Get a {@link ConfigurationPropertySources} instance for the specified
* {@link PropertySources} (either previously {@link #attach(MutablePropertySources)
* attached} or a new instance.
* @param propertySources the source property sources
* @return a {@link ConfigurationPropertySources} instance
*/
public static ConfigurationPropertySources get(PropertySources propertySources) {
if (propertySources == null) {
return null;
}
PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);
if (source != null) {
return (ConfigurationPropertySources) source.getSource();
}
return new ConfigurationPropertySources(propertySources);
}
/**
* Get a {@link ConfigurationPropertySources} instance for the {@link PropertySources}
* from the specified {@link ConfigurableEnvironment}, (either previously
* {@link #attach(MutablePropertySources) attached} or a new instance.
* @param environment the configurable environment
* @return a {@link ConfigurationPropertySources} instance
*/
public static ConfigurationPropertySources get(ConfigurableEnvironment environment) {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);
if (source != null) {
return (ConfigurationPropertySources) source.getSource();
}
return new ConfigurationPropertySources(propertySources);
private static boolean isIncluded(PropertySource<?> source) {
return !(source instanceof StubPropertySource)
&& !(source instanceof ConfigurationPropertySourcesPropertySource);
}
}

View File

@ -16,10 +16,6 @@
package org.springframework.boot.context.properties.source;
import java.util.Objects;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.core.env.Environment;
@ -70,10 +66,14 @@ class ConfigurationPropertySourcesPropertySource
if (name == null) {
return null;
}
Stream<ConfigurationPropertySource> sources = StreamSupport
.stream(getSource().spliterator(), false);
return sources.map(source -> source.getConfigurationProperty(name))
.filter(Objects::nonNull).findFirst().orElse(null);
for (ConfigurationPropertySource configurationPropertySource : getSource()) {
ConfigurationProperty configurationProperty = configurationPropertySource
.getConfigurationProperty(name);
if (configurationProperty != null) {
return configurationProperty;
}
}
return null;
}
}

View File

@ -17,11 +17,10 @@
package org.springframework.boot.context.properties.source;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.env.PropertySource;
import org.springframework.util.ObjectUtils;
/**
* Default {@link PropertyMapper} implementation. Names are mapped by removing invalid
@ -31,36 +30,48 @@ import org.springframework.core.env.PropertySource;
* @author Phillip Webb
* @author Madhura Bhave
* @see PropertyMapper
* @see PropertySourceConfigurationPropertySource
* @see SpringConfigurationPropertySource
*/
class DefaultPropertyMapper implements PropertyMapper {
final class DefaultPropertyMapper implements PropertyMapper {
public static final PropertyMapper INSTANCE = new DefaultPropertyMapper();
private final Cache<ConfigurationPropertyName> configurationPropertySourceCache = new Cache<>();
private LastMapping<ConfigurationPropertyName> lastMappedConfigurationPropertyName;
private final Cache<String> propertySourceCache = new Cache<>();
private LastMapping<String> lastMappedPropertyName;
private final ConfigurationPropertyNameBuilder nameBuilder = new ConfigurationPropertyNameBuilder();
private DefaultPropertyMapper() {
}
@Override
public List<PropertyMapping> map(PropertySource<?> propertySource,
ConfigurationPropertyName configurationPropertyName) {
List<PropertyMapping> mapping = this.configurationPropertySourceCache
.get(configurationPropertyName);
if (mapping == null) {
String convertedName = configurationPropertyName.toString();
mapping = Collections.singletonList(
new PropertyMapping(convertedName, configurationPropertyName));
this.configurationPropertySourceCache.put(configurationPropertyName, mapping);
// Use a local copy in case another thread changes things
LastMapping<ConfigurationPropertyName> last = this.lastMappedConfigurationPropertyName;
if (last != null && last.isFrom(configurationPropertyName)) {
return last.getMapping();
}
String convertedName = configurationPropertyName.toString();
List<PropertyMapping> mapping = Collections.singletonList(
new PropertyMapping(convertedName, configurationPropertyName));
this.lastMappedConfigurationPropertyName = new LastMapping<>(
configurationPropertyName, mapping);
return mapping;
}
@Override
public List<PropertyMapping> map(PropertySource<?> propertySource,
String propertySourceName) {
return this.propertySourceCache.computeIfAbsent(propertySourceName, this::tryMap);
// Use a local copy in case another thread changes things
LastMapping<String> last = this.lastMappedPropertyName;
if (last != null && last.isFrom(propertySourceName)) {
return last.getMapping();
}
List<PropertyMapping> mapping = tryMap(propertySourceName);
this.lastMappedPropertyName = new LastMapping<>(propertySourceName, mapping);
return mapping;
}
private List<PropertyMapping> tryMap(String propertySourceName) {
@ -75,23 +86,23 @@ class DefaultPropertyMapper implements PropertyMapper {
}
}
private static class Cache<K> extends LinkedHashMap<K, List<PropertyMapping>> {
private static class LastMapping<T> {
private final int capacity;
private final T from;
Cache() {
this(1);
private final List<PropertyMapping> mapping;
LastMapping(T from, List<PropertyMapping> mapping) {
this.from = from;
this.mapping = mapping;
}
Cache(int capacity) {
super(capacity, (float) 0.75, true);
this.capacity = capacity;
public boolean isFrom(T from) {
return ObjectUtils.nullSafeEquals(from, this.from);
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, List<PropertyMapping>> eldest) {
return size() > this.capacity;
public List<PropertyMapping> getMapping() {
return this.mapping;
}
}

View File

@ -31,7 +31,6 @@ import org.springframework.util.Assert;
*
* @author Phillip Webb
* @author Madhura Bhave
* @since 2.0.0
*/
public class MapConfigurationPropertySource
implements IterableConfigurationPropertySource {
@ -54,9 +53,9 @@ public class MapConfigurationPropertySource
*/
public MapConfigurationPropertySource(Map<?, ?> map) {
this.source = new LinkedHashMap<>();
this.delegate = new PropertySourceIterableConfigurationPropertySource(
this.delegate = new SpringIterableConfigurationPropertySource(
new MapPropertySource("source", this.source),
new DefaultPropertyMapper());
DefaultPropertyMapper.INSTANCE);
putAll(map);
}

View File

@ -27,16 +27,16 @@ import org.springframework.core.env.PropertySource;
* <P>
* Mappings should be provided for both {@link ConfigurationPropertyName
* ConfigurationPropertyName} types and {@code String} based names. This allows the
* {@link PropertySourceConfigurationPropertySource} to first attempt any direct mappings
* {@link SpringConfigurationPropertySource} to first attempt any direct mappings
* (i.e. map the {@link ConfigurationPropertyName} directly to the {@link PropertySource}
* name) before falling back to {@link EnumerablePropertySource enumerating} property
* names, mapping them to a {@link ConfigurationPropertyName} and checking for
* {@link PropertyMapping#isApplicable(ConfigurationPropertyName) applicability}. See
* {@link PropertySourceConfigurationPropertySource} for more details.
* {@link SpringConfigurationPropertySource} for more details.
*
* @author Phillip Webb
* @author Madhura Bhave
* @see PropertySourceConfigurationPropertySource
* @see SpringConfigurationPropertySource
*/
interface PropertyMapper {

View File

@ -26,7 +26,7 @@ import org.springframework.core.env.PropertySource;
*
* @author Phillip Webb
* @author Madhura Bhave
* @see PropertySourceConfigurationPropertySource
* @see SpringConfigurationPropertySource
*/
class PropertyMapping {

View File

@ -18,14 +18,17 @@ package org.springframework.boot.context.properties.source;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.boot.env.RandomValuePropertySource;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.PropertySourceOrigin;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import org.springframework.util.Assert;
/**
@ -41,16 +44,19 @@ import org.springframework.util.Assert;
* {@link ConfigurationPropertyName} to one or more {@code String} based names. This
* allows fast property resolution for well formed property sources.
* <p>
* If at all possible the {@link PropertySourceIterableConfigurationPropertySource} should
* be used in preference to this implementation since it supports "relaxed" style
* resolution.
* When possible the {@link SpringIterableConfigurationPropertySource} will be used in
* preference to this implementation since it supports full "relaxed" style resolution.
*
* @author Phillip Webb
* @author Madhura Bhave
* @see #from(PropertySource)
* @see PropertyMapper
* @see PropertySourceIterableConfigurationPropertySource
* @see SpringIterableConfigurationPropertySource
*/
class PropertySourceConfigurationPropertySource implements ConfigurationPropertySource {
class SpringConfigurationPropertySource implements ConfigurationPropertySource {
private static final ConfigurationPropertyName RANDOM = ConfigurationPropertyName
.of("random");
private final PropertySource<?> propertySource;
@ -59,21 +65,21 @@ class PropertySourceConfigurationPropertySource implements ConfigurationProperty
private final Function<ConfigurationPropertyName, Optional<Boolean>> containsDescendantOfMethod;
/**
* Create a new {@link PropertySourceConfigurationPropertySource} implementation.
* Create a new {@link SpringConfigurationPropertySource} implementation.
* @param propertySource the source property source
* @param mapper the property mapper
* @param containsDescendantOfMethod function used to implement
* {@link #containsDescendantOf(ConfigurationPropertyName)} (may be {@code null})
*/
PropertySourceConfigurationPropertySource(PropertySource<?> propertySource,
SpringConfigurationPropertySource(PropertySource<?> propertySource,
PropertyMapper mapper,
Function<ConfigurationPropertyName, Optional<Boolean>> containsDescendantOfMethod) {
Assert.notNull(propertySource, "PropertySource must not be null");
Assert.notNull(mapper, "Mapper must not be null");
this.propertySource = propertySource;
this.mapper = new ExceptionSwallowingPropertyMapper(mapper);
this.containsDescendantOfMethod = (containsDescendantOfMethod != null ? containsDescendantOfMethod
: (n) -> Optional.empty());
this.containsDescendantOfMethod = (containsDescendantOfMethod != null
? containsDescendantOfMethod : (n) -> Optional.empty());
}
@Override
@ -120,6 +126,62 @@ class PropertySourceConfigurationPropertySource implements ConfigurationProperty
return this.propertySource.toString();
}
/**
* Create a new {@link SpringConfigurationPropertySource} for the specified
* {@link PropertySource}.
* @param source the source Spring {@link PropertySource}
* @return a {@link SpringConfigurationPropertySource} or
* {@link SpringIterableConfigurationPropertySource} instance
*/
public static SpringConfigurationPropertySource from(PropertySource<?> source) {
Assert.notNull(source, "Source must not be null");
PropertyMapper mapper = getPropertyMapper(source);
if (isFullEnumerable(source)) {
return new SpringIterableConfigurationPropertySource(
(EnumerablePropertySource<?>) source, mapper);
}
return new SpringConfigurationPropertySource(source, mapper,
getContainsDescendantOfMethod(source));
}
private static PropertyMapper getPropertyMapper(PropertySource<?> source) {
if (source instanceof SystemEnvironmentPropertySource) {
return SystemEnvironmentPropertyMapper.INSTANCE;
}
return DefaultPropertyMapper.INSTANCE;
}
private static boolean isFullEnumerable(PropertySource<?> source) {
PropertySource<?> rootSource = getRootSource(source);
if (rootSource.getSource() instanceof Map) {
// Check we're not security restricted
try {
((Map<?, ?>) rootSource.getSource()).size();
}
catch (UnsupportedOperationException ex) {
return false;
}
}
return (source instanceof EnumerablePropertySource);
}
private static PropertySource<?> getRootSource(PropertySource<?> source) {
while (source.getSource() != null
&& source.getSource() instanceof PropertySource) {
source = (PropertySource<?>) source.getSource();
}
return source;
}
private static Function<ConfigurationPropertyName, Optional<Boolean>> getContainsDescendantOfMethod(
PropertySource<?> source) {
if (source instanceof RandomValuePropertySource) {
return (name) -> Optional
.of(name.isAncestorOf(RANDOM) || name.equals(RANDOM));
}
return null;
}
/**
* {@link PropertyMapper} that swallows exceptions when the mapping fails.
*/

View File

@ -0,0 +1,160 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.properties.source;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySource.StubPropertySource;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Adapter to convert Spring's {@link MutablePropertySources} to
* {@link ConfigurationPropertySource ConfigurationPropertySources}.
*
* @author Phillip Webb
*/
class SpringConfigurationPropertySources
implements Iterable<ConfigurationPropertySource> {
private final MutablePropertySources sources;
private volatile PropertySourcesKey lastKey;
private volatile List<ConfigurationPropertySource> adaptedSources;
SpringConfigurationPropertySources(MutablePropertySources sources) {
Assert.notNull(sources, "Sources must not be null");
this.sources = sources;
}
@Override
public Iterator<ConfigurationPropertySource> iterator() {
checkForChanges();
return this.adaptedSources.iterator();
}
private void checkForChanges() {
PropertySourcesKey lastKey = this.lastKey;
PropertySourcesKey currentKey = new PropertySourcesKey(this.sources);
if (!currentKey.equals(lastKey)) {
onChange(this.sources);
this.lastKey = currentKey;
}
}
private void onChange(MutablePropertySources sources) {
this.adaptedSources = streamPropertySources(sources)
.map(SpringConfigurationPropertySource::from)
.collect(Collectors.toList());
}
private Stream<PropertySource<?>> streamPropertySources(
Iterable<PropertySource<?>> sources) {
return StreamSupport.stream(sources.spliterator(), false).flatMap(this::flatten)
.filter(this::isIncluded);
}
private Stream<PropertySource<?>> flatten(PropertySource<?> source) {
if (source.getSource() instanceof ConfigurableEnvironment) {
return streamPropertySources(
((ConfigurableEnvironment) source.getSource()).getPropertySources());
}
return Stream.of(source);
}
private boolean isIncluded(PropertySource<?> source) {
return !(source instanceof StubPropertySource)
&& !(source instanceof ConfigurationPropertySourcesPropertySource);
}
private static class PropertySourcesKey {
private final List<PropertySourceKey> keys = new ArrayList<>();
PropertySourcesKey(MutablePropertySources sources) {
sources.forEach(this::addKey);
}
private void addKey(PropertySource<?> source) {
this.keys.add(new PropertySourceKey(source));
}
@Override
public int hashCode() {
return this.keys.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return this.keys.equals(((PropertySourcesKey) obj).keys);
}
}
private static class PropertySourceKey {
private final String name;
private final Class<?> type;
PropertySourceKey(PropertySource<?> source) {
this.name = source.getName();
this.type = source.getClass();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
result = prime * result + ObjectUtils.nullSafeHashCode(this.type);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
PropertySourceKey other = (PropertySourceKey) obj;
boolean result = true;
result = result && ObjectUtils.nullSafeEquals(this.name, other.name);
result = result && ObjectUtils.nullSafeEquals(this.type, other.type);
return result;
}
}
}

View File

@ -31,21 +31,24 @@ import org.springframework.util.ObjectUtils;
/**
* {@link ConfigurationPropertySource} backed by a {@link EnumerablePropertySource}.
* Extends {@link PropertySourceConfigurationPropertySource} with full "relaxed" mapping
* support. In order to use this adapter the underlying {@link PropertySource} must be
* fully enumerable. A security restricted {@link SystemEnvironmentPropertySource} cannot
* be adapted.
* Extends {@link SpringConfigurationPropertySource} with full "relaxed" mapping support.
* In order to use this adapter the underlying {@link PropertySource} must be fully
* enumerable. A security restricted {@link SystemEnvironmentPropertySource} cannot be
* adapted.
*
* @author Phillip Webb
* @author Madhura Bhave
* @see PropertyMapper
*/
class PropertySourceIterableConfigurationPropertySource
extends PropertySourceConfigurationPropertySource
class SpringIterableConfigurationPropertySource extends SpringConfigurationPropertySource
implements IterableConfigurationPropertySource {
PropertySourceIterableConfigurationPropertySource(
EnumerablePropertySource<?> propertySource, PropertyMapper mapper) {
private volatile Object cacheKey;
private volatile Cache cache;
SpringIterableConfigurationPropertySource(EnumerablePropertySource<?> propertySource,
PropertyMapper mapper) {
super(propertySource, mapper, null);
assertEnumerablePropertySource(propertySource);
}
@ -63,10 +66,6 @@ class PropertySourceIterableConfigurationPropertySource
}
}
private volatile Object cacheKey;
private volatile Cache cache;
@Override
public ConfigurationProperty getConfigurationProperty(
ConfigurationPropertyName name) {

View File

@ -41,15 +41,18 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb
* @author Madhura Bhave
* @see PropertyMapper
* @see PropertySourceConfigurationPropertySource
* @see SpringConfigurationPropertySource
*/
class SystemEnvironmentPropertyMapper implements PropertyMapper {
final class SystemEnvironmentPropertyMapper implements PropertyMapper {
public static final PropertyMapper INSTANCE = new SystemEnvironmentPropertyMapper();
private final ConfigurationPropertyNameBuilder nameBuilder = new ConfigurationPropertyNameBuilder(
this::createElement);
private SystemEnvironmentPropertyMapper() {
}
@Override
public List<PropertyMapping> map(PropertySource<?> propertySource,
String propertySourceName) {

View File

@ -48,7 +48,6 @@ import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEven
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.testutil.InternalOutputCapture;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
@ -868,8 +867,7 @@ public class SpringApplicationTests {
assertThat(this.context.getEnvironment().getProperty("foo")).isEqualTo("bar");
Iterator<PropertySource<?>> iterator = this.context.getEnvironment()
.getPropertySources().iterator();
assertThat(iterator.next().getName())
.isEqualTo(ConfigurationPropertySources.PROPERTY_SOURCE_NAME);
assertThat(iterator.next().getName()).isEqualTo("configurationProperties");
assertThat(iterator.next().getName()).isEqualTo(
TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
}

View File

@ -105,7 +105,7 @@ public class LoggingApplicationListenerTests {
new File("target/foo.log").delete();
new File(tmpDir() + "/spring.log").delete();
ConfigurableEnvironment environment = this.context.getEnvironment();
ConfigurationPropertySources.attach(environment.getPropertySources());
ConfigurationPropertySources.attach(environment);
}
@After

View File

@ -17,7 +17,6 @@
package org.springframework.boot.context.properties.source;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@ -25,7 +24,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.env.RandomValuePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
@ -41,169 +40,47 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link ConfigurationPropertySources}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
public class ConfigurationPropertySourcesTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void createWhenPropertySourcesIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("PropertySources must not be null");
new ConfigurationPropertySources(null);
}
@Test
public void iteratorShouldAdaptPropertySource() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(new MapPropertySource("test",
Collections.<String, Object>singletonMap("a", "b")));
Iterator<ConfigurationPropertySource> iterator = new ConfigurationPropertySources(
sources).iterator();
assertThat(iterator.next()
.getConfigurationProperty(ConfigurationPropertyName.of("a")).getValue())
.isEqualTo("b");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void iteratorShouldAdaptSystemEnvironmentPropertySource() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addLast(new SystemEnvironmentPropertySource("system",
Collections.<String, Object>singletonMap("SERVER_PORT", "1234")));
Iterator<ConfigurationPropertySource> iterator = new ConfigurationPropertySources(
sources).iterator();
assertThat(
iterator.next()
.getConfigurationProperty(
ConfigurationPropertyName.of("server.port"))
.getValue()).isEqualTo("1234");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void iteratorShouldAdaptMultiplePropertySources() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addLast(new SystemEnvironmentPropertySource("system",
Collections.<String, Object>singletonMap("SERVER_PORT", "1234")));
sources.addLast(new MapPropertySource("test1",
Collections.<String, Object>singletonMap("server.po-rt", "4567")));
sources.addLast(new MapPropertySource("test2",
Collections.<String, Object>singletonMap("a", "b")));
Iterator<ConfigurationPropertySource> iterator = new ConfigurationPropertySources(
sources).iterator();
assertThat(
iterator.next()
.getConfigurationProperty(
ConfigurationPropertyName.of("server.port"))
.getValue()).isEqualTo("1234");
assertThat(
iterator.next()
.getConfigurationProperty(
ConfigurationPropertyName.of("server.port"))
.getValue()).isEqualTo("4567");
assertThat(iterator.next()
.getConfigurationProperty(ConfigurationPropertyName.of("a")).getValue())
.isEqualTo("b");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void attachShouldAddAdapterAtBeginning() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
ConfigurableEnvironment environment = new StandardEnvironment();
MutablePropertySources sources = environment.getPropertySources();
sources.addLast(new SystemEnvironmentPropertySource("system",
Collections.<String, Object>singletonMap("SERVER_PORT", "1234")));
sources.addLast(new MapPropertySource("config",
Collections.<String, Object>singletonMap("server.port", "4568")));
assertThat(sources.size()).isEqualTo(2);
ConfigurationPropertySources.attach(sources);
int size = sources.size();
ConfigurationPropertySources.attach(environment);
assertThat(sources.size()).isEqualTo(size + 1);
PropertyResolver resolver = new PropertySourcesPropertyResolver(sources);
assertThat(resolver.getProperty("server.port")).isEqualTo("1234");
assertThat(sources.size()).isEqualTo(3);
}
@Test
public void getWhenNotAttachedShouldReturnAdapted() throws Exception {
ConfigurableEnvironment environment = new StandardEnvironment();
assertThat(ConfigurationPropertySources.get(environment)).isNotEmpty();
}
@Test
public void getWhenAttachedShouldReturnAttached() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
ConfigurableEnvironment environment = new StandardEnvironment();
MutablePropertySources sources = environment.getPropertySources();
sources.addFirst(new MapPropertySource("test",
Collections.<String, Object>singletonMap("a", "b")));
ConfigurationPropertySources attached = ConfigurationPropertySources
.attach(sources);
assertThat(ConfigurationPropertySources.get(sources)).isSameAs(attached);
int expectedSize = sources.size();
ConfigurationPropertySources.attach(environment);
assertThat(ConfigurationPropertySources.get(environment)).hasSize(expectedSize);
}
@Test
public void getWhenNotAttachedShouldReturnNew() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(new MapPropertySource("test",
Collections.<String, Object>singletonMap("a", "b")));
assertThat(ConfigurationPropertySources.get(sources)).isNotNull();
assertThat(sources.size()).isEqualTo(1);
}
@Test
public void getWhenNonEnumerableShouldNotBeIterable() throws Exception {
StandardEnvironment environment = new StandardEnvironment();
Map<String, Object> source = new LinkedHashMap<String, Object>() {
@Override
public int size() {
throw new UnsupportedOperationException("Same as security restricted");
}
};
PropertySource<?> propertySource = new MapPropertySource("test", source);
environment.getPropertySources().addFirst(propertySource);
ConfigurationPropertySources sources = ConfigurationPropertySources
.get(environment);
ConfigurationPropertySource configurationPropertySource = sources.iterator()
.next();
assertThat(configurationPropertySource)
.isNotInstanceOf(IterableConfigurationPropertySource.class);
}
@Test
public void getWhenEnumerableButRestrictedShouldNotBeIterable() throws Exception {
StandardEnvironment environment = new StandardEnvironment();
PropertySource<?> propertySource = new PropertySource<Object>("test",
new Object()) {
@Override
public Object getProperty(String name) {
return null;
}
};
environment.getPropertySources().addFirst(propertySource);
ConfigurationPropertySources sources = ConfigurationPropertySources
.get(environment);
ConfigurationPropertySource configurationPropertySource = sources.iterator()
.next();
assertThat(configurationPropertySource)
.isNotInstanceOf(IterableConfigurationPropertySource.class);
}
@Test
public void getWhenEnumerableShouldBeIterable() throws Exception {
StandardEnvironment environment = new StandardEnvironment();
Map<String, Object> source = new LinkedHashMap<>();
source.put("fooBar", "Spring ${barBaz} ${bar-baz}");
source.put("barBaz", "Boot");
PropertySource<?> propertySource = new MapPropertySource("test", source);
environment.getPropertySources().addFirst(propertySource);
ConfigurationPropertySources sources = ConfigurationPropertySources
.get(environment);
ConfigurationPropertySource configurationPropertySource = sources.iterator()
.next();
assertThat(configurationPropertySource)
.isInstanceOf(IterableConfigurationPropertySource.class);
}
@Test
public void environmentPropertyExpansionShouldWorkWhenAttached() throws Exception {
StandardEnvironment environment = new StandardEnvironment();
public void environmentProperyExpansionShouldWorkWhenAttached() throws Exception {
ConfigurableEnvironment environment = new StandardEnvironment();
Map<String, Object> source = new LinkedHashMap<>();
source.put("fooBar", "Spring ${barBaz} ${bar-baz}");
source.put("barBaz", "Boot");
@ -214,7 +91,18 @@ public class ConfigurationPropertySourcesTests {
}
@Test
public void environmentSourceShouldBeFlattened() throws Exception {
public void fromPropertySourceShouldReturnSpringConfigurationPropertySource()
throws Exception {
PropertySource<?> source = new MapPropertySource("foo",
Collections.<String, Object>singletonMap("foo", "bar"));
ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySources
.from(source).iterator().next();
assertThat(configurationPropertySource)
.isInstanceOf(SpringConfigurationPropertySource.class);
}
@Test
public void fromPropertySourcseShouldFlattenPropertySources() throws Exception {
StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addFirst(new MapPropertySource("foo",
Collections.<String, Object>singletonMap("foo", "bar")));
@ -231,27 +119,9 @@ public class ConfigurationPropertySourcesTests {
});
sources.addLast(new MapPropertySource("baz",
Collections.<String, Object>singletonMap("baz", "barf")));
ConfigurationPropertySources configurationSources = ConfigurationPropertySources
.get(sources);
Iterable<ConfigurationPropertySource> configurationSources = ConfigurationPropertySources
.from(sources);
assertThat(configurationSources.iterator()).hasSize(5);
}
@Test
public void containsDescendantOfForRandomSourceShouldDetectNamesStartingRandom()
throws Exception {
StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addFirst(new RandomValuePropertySource());
ConfigurationPropertySource source = ConfigurationPropertySources.get(environment)
.iterator().next();
assertThat(source.containsDescendantOf(ConfigurationPropertyName.of("")))
.contains(true);
assertThat(source.containsDescendantOf(ConfigurationPropertyName.of("random")))
.contains(true);
assertThat(source.containsDescendantOf(ConfigurationPropertyName.of("other")))
.contains(false);
assertThat(
source.containsDescendantOf(ConfigurationPropertyName.of("random.foo")))
.contains(false);
}
}

View File

@ -28,11 +28,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class DefaultPropertyMapperTests extends AbstractPropertyMapperTests {
private DefaultPropertyMapper mapper = new DefaultPropertyMapper();
@Override
protected PropertyMapper getMapper() {
return this.mapper;
return DefaultPropertyMapper.INSTANCE;
}
@Test

View File

@ -32,12 +32,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PropertySourceConfigurationPropertySource}.
* Tests for {@link SpringConfigurationPropertySource}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
public class PropertySourceConfigurationPropertySourceTests {
public class SpringConfigurationPropertySourceTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@ -46,16 +46,14 @@ public class PropertySourceConfigurationPropertySourceTests {
public void createWhenPropertySourceIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("PropertySource must not be null");
new PropertySourceConfigurationPropertySource(null, mock(PropertyMapper.class),
null);
new SpringConfigurationPropertySource(null, mock(PropertyMapper.class), null);
}
@Test
public void createWhenMapperIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Mapper must not be null");
new PropertySourceConfigurationPropertySource(mock(PropertySource.class), null,
null);
new SpringConfigurationPropertySource(mock(PropertySource.class), null, null);
}
@Test
@ -68,7 +66,7 @@ public class PropertySourceConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key2");
PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource(
SpringConfigurationPropertySource adapter = new SpringConfigurationPropertySource(
propertySource, mapper, null);
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("value2");
}
@ -82,7 +80,7 @@ public class PropertySourceConfigurationPropertySourceTests {
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key",
(value) -> value.toString().replace("ue", "let"));
PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource(
SpringConfigurationPropertySource adapter = new SpringConfigurationPropertySource(
propertySource, mapper, null);
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("vallet");
}
@ -95,7 +93,7 @@ public class PropertySourceConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key");
PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource(
SpringConfigurationPropertySource adapter = new SpringConfigurationPropertySource(
propertySource, mapper, null);
assertThat(adapter.getConfigurationProperty(name).getOrigin().toString())
.isEqualTo("\"key\" from property source \"test\"");
@ -110,7 +108,7 @@ public class PropertySourceConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key");
PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource(
SpringConfigurationPropertySource adapter = new SpringConfigurationPropertySource(
propertySource, mapper, null);
assertThat(adapter.getConfigurationProperty(name).getOrigin().toString())
.isEqualTo("TestOrigin key");
@ -121,12 +119,61 @@ public class PropertySourceConfigurationPropertySourceTests {
Map<String, Object> source = new LinkedHashMap<>();
source.put("foo.bar", "value");
PropertySource<?> propertySource = new MapPropertySource("test", source);
PropertySourceConfigurationPropertySource adapter = new PropertySourceConfigurationPropertySource(
propertySource, new DefaultPropertyMapper(), null);
SpringConfigurationPropertySource adapter = new SpringConfigurationPropertySource(
propertySource, DefaultPropertyMapper.INSTANCE, null);
assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("foo")))
.isEmpty();
}
@Test
public void fromWhenPropertySourceIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Source must not be null");
SpringConfigurationPropertySource.from(null);
}
@Test
public void fromWhenNonEnumerableShouldReturnNonIterable() throws Exception {
PropertySource<?> propertySource = new PropertySource<Object>("test",
new Object()) {
@Override
public Object getProperty(String name) {
return null;
}
};
assertThat(SpringConfigurationPropertySource.from(propertySource))
.isNotInstanceOf(IterableConfigurationPropertySource.class);
}
@Test
public void fromWhenEnumerableButRestrictedShouldReturnNonIterable()
throws Exception {
Map<String, Object> source = new LinkedHashMap<String, Object>() {
@Override
public int size() {
throw new UnsupportedOperationException("Same as security restricted");
}
};
PropertySource<?> propertySource = new MapPropertySource("test", source);
assertThat(SpringConfigurationPropertySource.from(propertySource))
.isNotInstanceOf(IterableConfigurationPropertySource.class);
}
@Test
public void getWhenEnumerableShouldBeIterable() throws Exception {
Map<String, Object> source = new LinkedHashMap<>();
source.put("fooBar", "Spring ${barBaz} ${bar-baz}");
source.put("barBaz", "Boot");
PropertySource<?> propertySource = new MapPropertySource("test", source);
assertThat(SpringConfigurationPropertySource.from(propertySource))
.isInstanceOf(IterableConfigurationPropertySource.class);
}
/**
* Test {@link PropertySource} that's also a {@link OriginLookup}.
*/

View File

@ -0,0 +1,135 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.properties.source;
import java.util.Collections;
import java.util.Iterator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SpringConfigurationPropertySources}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
public class SpringConfigurationPropertySourcesTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void createWhenPropertySourcesIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Sources must not be null");
new SpringConfigurationPropertySources(null);
}
@Test
public void shouldAdaptPropertySource() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(new MapPropertySource("test",
Collections.<String, Object>singletonMap("a", "b")));
Iterator<ConfigurationPropertySource> iterator = new SpringConfigurationPropertySources(
sources).iterator();
ConfigurationPropertyName name = ConfigurationPropertyName.of("a");
assertThat(iterator.next().getConfigurationProperty(name).getValue())
.isEqualTo("b");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAdaptSystemEnvironmentPropertySource() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addLast(new SystemEnvironmentPropertySource("system",
Collections.<String, Object>singletonMap("SERVER_PORT", "1234")));
Iterator<ConfigurationPropertySource> iterator = new SpringConfigurationPropertySources(
sources).iterator();
ConfigurationPropertyName name = ConfigurationPropertyName.of("server.port");
assertThat(iterator.next().getConfigurationProperty(name).getValue())
.isEqualTo("1234");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldAdaptMultiplePropertySources() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addLast(new SystemEnvironmentPropertySource("system",
Collections.<String, Object>singletonMap("SERVER_PORT", "1234")));
sources.addLast(new MapPropertySource("test1",
Collections.<String, Object>singletonMap("server.po-rt", "4567")));
sources.addLast(new MapPropertySource("test2",
Collections.<String, Object>singletonMap("a", "b")));
Iterator<ConfigurationPropertySource> iterator = new SpringConfigurationPropertySources(
sources).iterator();
ConfigurationPropertyName name = ConfigurationPropertyName.of("server.port");
assertThat(iterator.next().getConfigurationProperty(name).getValue())
.isEqualTo("1234");
assertThat(iterator.next().getConfigurationProperty(name).getValue())
.isEqualTo("4567");
assertThat(iterator.next()
.getConfigurationProperty(ConfigurationPropertyName.of("a")).getValue())
.isEqualTo("b");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void shouldFlattenEnvironment() throws Exception {
StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addFirst(new MapPropertySource("foo",
Collections.<String, Object>singletonMap("foo", "bar")));
environment.getPropertySources().addFirst(new MapPropertySource("far",
Collections.<String, Object>singletonMap("far", "far")));
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(new PropertySource<Environment>("env", environment) {
@Override
public String getProperty(String key) {
return this.source.getProperty(key);
}
});
sources.addLast(new MapPropertySource("baz",
Collections.<String, Object>singletonMap("baz", "barf")));
SpringConfigurationPropertySources configurationSources = new SpringConfigurationPropertySources(
sources);
assertThat(configurationSources.iterator()).hasSize(5);
}
@Test
public void shouldTrackChanges() throws Exception {
MutablePropertySources sources = new MutablePropertySources();
sources.addLast(new MapPropertySource("test1",
Collections.<String, Object>singletonMap("a", "b")));
assertThat(new SpringConfigurationPropertySources(sources).iterator()).hasSize(1);
sources.addLast(new MapPropertySource("test2",
Collections.<String, Object>singletonMap("b", "c")));
assertThat(new SpringConfigurationPropertySources(sources).iterator()).hasSize(2);
}
}

View File

@ -33,12 +33,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PropertySourceIterableConfigurationPropertySource}.
* Tests for {@link SpringIterableConfigurationPropertySource}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
public class PropertySourceIterableConfigurationPropertySourceTests {
public class SpringIterableConfigurationPropertySourceTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@ -47,15 +47,14 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
public void createWhenPropertySourceIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("PropertySource must not be null");
new PropertySourceIterableConfigurationPropertySource(null,
mock(PropertyMapper.class));
new SpringIterableConfigurationPropertySource(null, mock(PropertyMapper.class));
}
@Test
public void createWhenMapperIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Mapper must not be null");
new PropertySourceIterableConfigurationPropertySource(
new SpringIterableConfigurationPropertySource(
mock(EnumerablePropertySource.class), null);
}
@ -72,7 +71,7 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
mapper.addFromPropertySource("key1", "my.key1");
mapper.addFromPropertySource("key2", "my.key2a", "my.key2b");
mapper.addFromPropertySource("key4", "my.key4");
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, mapper);
assertThat(adapter.iterator()).extracting(Object::toString)
.containsExactly("my.key1", "my.key2a", "my.key2b", "my.key4");
@ -89,7 +88,7 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key2");
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, mapper);
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("value2");
}
@ -105,7 +104,7 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
mapper.addFromPropertySource("key1", "my.missing");
mapper.addFromPropertySource("key2", "my.k-e-y");
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, mapper);
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("value2");
@ -121,7 +120,7 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key",
(value) -> value.toString().replace("ue", "let"));
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, mapper);
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("vallet");
}
@ -135,7 +134,7 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key");
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, mapper);
assertThat(adapter.getConfigurationProperty(name).getOrigin().toString())
.isEqualTo("\"key\" from property source \"test\"");
@ -150,7 +149,7 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
TestPropertyMapper mapper = new TestPropertyMapper();
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
mapper.addFromConfigurationProperty(name, "key");
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, mapper);
assertThat(adapter.getConfigurationProperty(name).getOrigin().toString())
.isEqualTo("TestOrigin key");
@ -163,8 +162,8 @@ public class PropertySourceIterableConfigurationPropertySourceTests {
source.put("faf", "value");
EnumerablePropertySource<?> propertySource = new OriginCapablePropertySource<>(
new MapPropertySource("test", source));
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
propertySource, new DefaultPropertyMapper());
SpringIterableConfigurationPropertySource adapter = new SpringIterableConfigurationPropertySource(
propertySource, DefaultPropertyMapper.INSTANCE);
assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("foo")))
.contains(true);
assertThat(adapter.containsDescendantOf(ConfigurationPropertyName.of("faf")))

View File

@ -36,11 +36,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapperTests {
private SystemEnvironmentPropertyMapper mapper = new SystemEnvironmentPropertyMapper();
@Override
protected PropertyMapper getMapper() {
return this.mapper;
return SystemEnvironmentPropertyMapper.INSTANCE;
}
@Test
@ -75,7 +73,7 @@ public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapper
Map<String, Object> source = new LinkedHashMap<>();
source.put("SERVER__", "foo,bar,baz");
PropertySource<?> propertySource = new MapPropertySource("test", source);
List<PropertyMapping> mappings = this.mapper.map(propertySource, "SERVER__");
List<PropertyMapping> mappings = getMapper().map(propertySource, "SERVER__");
List<Object> result = new ArrayList<>();
for (PropertyMapping mapping : mappings) {
Object value = propertySource.getProperty(mapping.getPropertySourceName());
@ -91,7 +89,7 @@ public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapper
Map<String, Object> source = new LinkedHashMap<>();
source.put("SERVER__", "foo,bar,baz");
PropertySource<?> propertySource = new MapPropertySource("test", source);
List<PropertyMapping> mappings = this.mapper.map(propertySource,
List<PropertyMapping> mappings = getMapper().map(propertySource,
ConfigurationPropertyName.of("server[1]"));
List<Object> result = new ArrayList<>();
for (PropertyMapping mapping : mappings) {
@ -108,7 +106,7 @@ public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapper
public void underscoreShouldNotMapToEmptyString() throws Exception {
Map<String, Object> source = new LinkedHashMap<>();
PropertySource<?> propertySource = new MapPropertySource("test", source);
List<PropertyMapping> mappings = this.mapper.map(propertySource, "_");
List<PropertyMapping> mappings = getMapper().map(propertySource, "_");
boolean applicable = false;
for (PropertyMapping mapping : mappings) {
applicable = mapping.isApplicable(ConfigurationPropertyName.of(""));
@ -120,7 +118,7 @@ public class SystemEnvironmentPropertyMapperTests extends AbstractPropertyMapper
public void underscoreWithWhitespaceShouldNotMapToEmptyString() throws Exception {
Map<String, Object> source = new LinkedHashMap<>();
PropertySource<?> propertySource = new MapPropertySource("test", source);
List<PropertyMapping> mappings = this.mapper.map(propertySource, " _");
List<PropertyMapping> mappings = getMapper().map(propertySource, " _");
boolean applicable = false;
for (PropertyMapping mapping : mappings) {
applicable = mapping.isApplicable(ConfigurationPropertyName.of(""));