Split ConfigurationPropertySource for iteration
Create separate `IterableConfigurationPropertySource` and `ConfigurationPropertySource` interfaces so that it's possible to work out if a source can truly iterate the values that it contains. Prior to this commit there was only a single `ConfigurationPropertySource` interface, which returned an empty Iterator when values could not be iterated. This design made it impossible to tell the difference between a source that was empty, and a source that could not be iterated. The `ConfigurationPropertySources` class has been updated to adapt non-enumerable and enumerable Spring PropertySources to the correct `ConfigurationPropertySource` interface. It also deals with the edge case of the `SystemPropertySource` running in a security restricted environment. Fixes gh-9057pull/9050/merge
parent
53fd1f7f2e
commit
10b8eb3109
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A {@link IterableConfigurationPropertySource} supporting name aliases.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class AliasedIterableConfigurationPropertySource
|
||||
extends AliasedConfigurationPropertySource
|
||||
implements IterableConfigurationPropertySource {
|
||||
|
||||
AliasedIterableConfigurationPropertySource(IterableConfigurationPropertySource source,
|
||||
ConfigurationPropertyNameAliases aliases) {
|
||||
super(source, aliases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<ConfigurationPropertyName> stream() {
|
||||
return StreamSupport.stream(getSource().spliterator(), false)
|
||||
.flatMap(this::addAliases);
|
||||
}
|
||||
|
||||
private Stream<ConfigurationPropertyName> addAliases(ConfigurationPropertyName name) {
|
||||
Stream<ConfigurationPropertyName> names = Stream.of(name);
|
||||
List<ConfigurationPropertyName> aliases = getAliases().getAliases(name);
|
||||
if (CollectionUtils.isEmpty(aliases)) {
|
||||
return names;
|
||||
}
|
||||
return Stream.concat(names, aliases.stream());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IterableConfigurationPropertySource getSource() {
|
||||
return (IterableConfigurationPropertySource) super.getSource();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* A filtered {@link IterableConfigurationPropertySource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class FilteredIterableConfigurationPropertiesSource
|
||||
extends FilteredConfigurationPropertiesSource
|
||||
implements IterableConfigurationPropertySource {
|
||||
|
||||
FilteredIterableConfigurationPropertiesSource(
|
||||
IterableConfigurationPropertySource source,
|
||||
Predicate<ConfigurationPropertyName> filter) {
|
||||
super(source, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<ConfigurationPropertyName> stream() {
|
||||
return StreamSupport.stream(getSource().spliterator(), false).filter(getFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IterableConfigurationPropertySource getSource() {
|
||||
return (IterableConfigurationPropertySource) super.getSource();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.Iterator;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.boot.origin.OriginTrackedValue;
|
||||
|
||||
/**
|
||||
* A {@link ConfigurationPropertySource} with a fully {@link Iterable} set of entries.
|
||||
* Implementations of this interface <strong>must</strong> be able to iterate over all
|
||||
* contained configuration properties. Any {@code non-null} result from
|
||||
* {@link #getConfigurationProperty(ConfigurationPropertyName)} must also have an
|
||||
* equivalent entry in the {@link #iterator() iterator}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
* @see ConfigurationPropertyName
|
||||
* @see OriginTrackedValue
|
||||
* @see #getConfigurationProperty(ConfigurationPropertyName)
|
||||
* @see #iterator()
|
||||
* @see #stream()
|
||||
*/
|
||||
public interface IterableConfigurationPropertySource
|
||||
extends ConfigurationPropertySource, Iterable<ConfigurationPropertyName> {
|
||||
|
||||
/**
|
||||
* Return an iterator for the {@link ConfigurationPropertyName names} managed by this
|
||||
* source. If it is not possible to determine the names an empty iterator may be
|
||||
* returned.
|
||||
* @return an iterator (never {@code null})
|
||||
*/
|
||||
@Override
|
||||
default Iterator<ConfigurationPropertyName> iterator() {
|
||||
return stream().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequential {@code Stream} for the {@link ConfigurationPropertyName names}
|
||||
* managed by this source. If it is not possible to determine the names an
|
||||
* {@link Stream#empty() empty stream} may be returned.
|
||||
* @return a stream of names (never {@code null})
|
||||
*/
|
||||
Stream<ConfigurationPropertyName> stream();
|
||||
|
||||
@Override
|
||||
default IterableConfigurationPropertySource filter(
|
||||
Predicate<ConfigurationPropertyName> filter) {
|
||||
return new FilteredIterableConfigurationPropertiesSource(this, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
default IterableConfigurationPropertySource withAliases(
|
||||
ConfigurationPropertyNameAliases aliases) {
|
||||
return new AliasedIterableConfigurationPropertySource(this, aliases);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
||||
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.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
* @see PropertyMapper
|
||||
*/
|
||||
class PropertySourceIterableConfigurationPropertySource
|
||||
extends PropertySourceConfigurationPropertySource
|
||||
implements IterableConfigurationPropertySource {
|
||||
|
||||
PropertySourceIterableConfigurationPropertySource(
|
||||
EnumerablePropertySource<?> propertySource, PropertyMapper mapper) {
|
||||
super(propertySource, mapper);
|
||||
assertEnumerablePropertySource(propertySource);
|
||||
}
|
||||
|
||||
private void assertEnumerablePropertySource(
|
||||
EnumerablePropertySource<?> propertySource) {
|
||||
if (getPropertySource() instanceof MapPropertySource) {
|
||||
try {
|
||||
((MapPropertySource) getPropertySource()).getSource().size();
|
||||
}
|
||||
catch (UnsupportedOperationException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"PropertySource must be fully enumerable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private volatile Object cacheKey;
|
||||
|
||||
private volatile Cache cache;
|
||||
|
||||
@Override
|
||||
public ConfigurationProperty getConfigurationProperty(
|
||||
ConfigurationPropertyName name) {
|
||||
ConfigurationProperty configurationProperty = super.getConfigurationProperty(
|
||||
name);
|
||||
if (configurationProperty == null) {
|
||||
configurationProperty = find(getPropertyMappings(), name);
|
||||
}
|
||||
return configurationProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<ConfigurationPropertyName> stream() {
|
||||
return getConfigurationPropertyNames().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ConfigurationPropertyName> iterator() {
|
||||
return getConfigurationPropertyNames().iterator();
|
||||
}
|
||||
|
||||
private List<ConfigurationPropertyName> getConfigurationPropertyNames() {
|
||||
Cache cache = getCache();
|
||||
List<ConfigurationPropertyName> names = (cache != null ? cache.getNames() : null);
|
||||
if (names != null) {
|
||||
return names;
|
||||
}
|
||||
List<PropertyMapping> mappings = getPropertyMappings();
|
||||
names = new ArrayList<>(mappings.size());
|
||||
for (PropertyMapping mapping : mappings) {
|
||||
names.add(mapping.getConfigurationPropertyName());
|
||||
}
|
||||
names = Collections.unmodifiableList(names);
|
||||
if (cache != null) {
|
||||
cache.setNames(names);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private List<PropertyMapping> getPropertyMappings() {
|
||||
Cache cache = getCache();
|
||||
List<PropertyMapping> mappings = (cache != null ? cache.getMappings() : null);
|
||||
if (mappings != null) {
|
||||
return mappings;
|
||||
}
|
||||
String[] names = getPropertySource().getPropertyNames();
|
||||
mappings = new ArrayList<>(names.length);
|
||||
for (String name : names) {
|
||||
mappings.addAll(getMapper().map(getPropertySource(), name));
|
||||
}
|
||||
mappings = Collections.unmodifiableList(mappings);
|
||||
if (cache != null) {
|
||||
cache.setMappings(mappings);
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private Cache getCache() {
|
||||
Object cacheKey = getCacheKey();
|
||||
if (cacheKey == null) {
|
||||
return null;
|
||||
}
|
||||
if (ObjectUtils.nullSafeEquals(cacheKey, this.cacheKey)) {
|
||||
return this.cache;
|
||||
}
|
||||
this.cache = new Cache();
|
||||
this.cacheKey = cacheKey;
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
private Object getCacheKey() {
|
||||
if (getPropertySource() instanceof MapPropertySource) {
|
||||
return ((MapPropertySource) getPropertySource()).getSource().keySet();
|
||||
}
|
||||
return getPropertySource().getPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EnumerablePropertySource<?> getPropertySource() {
|
||||
return (EnumerablePropertySource<?>) super.getPropertySource();
|
||||
}
|
||||
|
||||
private static class Cache {
|
||||
|
||||
private List<ConfigurationPropertyName> names;
|
||||
|
||||
private List<PropertyMapping> mappings;
|
||||
|
||||
public List<ConfigurationPropertyName> getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
public void setNames(List<ConfigurationPropertyName> names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public List<PropertyMapping> getMappings() {
|
||||
return this.mappings;
|
||||
}
|
||||
|
||||
public void setMappings(List<PropertyMapping> mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AliasedConfigurationPropertySource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class AliasedIterableConfigurationPropertySourceTests
|
||||
extends AliasedConfigurationPropertySourceTests {
|
||||
|
||||
@Test
|
||||
public void streamShouldInclueAliases() throws Exception {
|
||||
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
|
||||
source.put("foo.bar", "bing");
|
||||
source.put("foo.baz", "biff");
|
||||
IterableConfigurationPropertySource aliased = source
|
||||
.withAliases(new ConfigurationPropertyNameAliases("foo.bar", "foo.bar1"));
|
||||
assertThat(aliased.stream()).containsExactly(
|
||||
ConfigurationPropertyName.of("foo.bar"),
|
||||
ConfigurationPropertyName.of("foo.bar1"),
|
||||
ConfigurationPropertyName.of("foo.baz"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for {@link FilteredIterableConfigurationPropertiesSource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class FilteredIterableConfigurationPropertiesSourceTests
|
||||
extends FilteredConfigurationPropertiesSourceTests {
|
||||
|
||||
@Test
|
||||
public void iteratorShouldFilterNames() throws Exception {
|
||||
MockConfigurationPropertySource source = (MockConfigurationPropertySource) createTestSource();
|
||||
IterableConfigurationPropertySource filtered = source.filter(this::noBrackets);
|
||||
assertThat(filtered.iterator()).extracting(ConfigurationPropertyName::toString)
|
||||
.containsExactly("a", "b", "c");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationPropertySource convertSource(
|
||||
MockConfigurationPropertySource source) {
|
||||
return source;
|
||||
}
|
||||
|
||||
private boolean noBrackets(ConfigurationPropertyName name) {
|
||||
return name.toString().indexOf("[") == -1;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.boot.origin.OriginLookup;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertySourceIterableConfigurationPropertySource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class PropertySourceIterableConfigurationPropertySourceTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenPropertySourceIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("PropertySource must not be null");
|
||||
new PropertySourceIterableConfigurationPropertySource(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(
|
||||
mock(EnumerablePropertySource.class), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void iteratorShouldAdaptNames() throws Exception {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
source.put("key1", "value1");
|
||||
source.put("key2", "value2");
|
||||
source.put("key3", "value3");
|
||||
source.put("key4", "value4");
|
||||
EnumerablePropertySource<?> propertySource = new MapPropertySource("test",
|
||||
source);
|
||||
TestPropertyMapper mapper = new TestPropertyMapper();
|
||||
mapper.addFromProperySource("key1", "my.key1");
|
||||
mapper.addFromProperySource("key2", "my.key2a", "my.key2b");
|
||||
mapper.addFromProperySource("key4", "my.key4");
|
||||
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
|
||||
propertySource, mapper);
|
||||
assertThat(adapter.iterator()).extracting(Object::toString)
|
||||
.containsExactly("my.key1", "my.key2a", "my.key2b", "my.key4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueShouldUseDirectMapping() throws Exception {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
source.put("key1", "value1");
|
||||
source.put("key2", "value2");
|
||||
source.put("key3", "value3");
|
||||
EnumerablePropertySource<?> propertySource = new MapPropertySource("test",
|
||||
source);
|
||||
TestPropertyMapper mapper = new TestPropertyMapper();
|
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
|
||||
mapper.addFromConfigurationProperty(name, "key2");
|
||||
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
|
||||
propertySource, mapper);
|
||||
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("value2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueShouldUseEnumerableMapping() throws Exception {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
source.put("key1", "value1");
|
||||
source.put("key2", "value2");
|
||||
source.put("key3", "value3");
|
||||
EnumerablePropertySource<?> propertySource = new MapPropertySource("test",
|
||||
source);
|
||||
TestPropertyMapper mapper = new TestPropertyMapper();
|
||||
mapper.addFromProperySource("key1", "my.missing");
|
||||
mapper.addFromProperySource("key2", "my.k-e-y");
|
||||
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
|
||||
propertySource, mapper);
|
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
|
||||
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("value2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueShouldUseExtractor() throws Exception {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
source.put("key", "value");
|
||||
EnumerablePropertySource<?> propertySource = new MapPropertySource("test",
|
||||
source);
|
||||
TestPropertyMapper mapper = new TestPropertyMapper();
|
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
|
||||
mapper.addFromConfigurationProperty(name, "key",
|
||||
(value) -> value.toString().replace("ue", "let"));
|
||||
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
|
||||
propertySource, mapper);
|
||||
assertThat(adapter.getConfigurationProperty(name).getValue()).isEqualTo("vallet");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueOrigin() throws Exception {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
source.put("key", "value");
|
||||
EnumerablePropertySource<?> propertySource = new MapPropertySource("test",
|
||||
source);
|
||||
TestPropertyMapper mapper = new TestPropertyMapper();
|
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
|
||||
mapper.addFromConfigurationProperty(name, "key");
|
||||
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
|
||||
propertySource, mapper);
|
||||
assertThat(adapter.getConfigurationProperty(name).getOrigin().toString())
|
||||
.isEqualTo("\"key\" from property source \"test\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueWhenOriginCapableShouldIncludeSourceOrigin() throws Exception {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
source.put("key", "value");
|
||||
EnumerablePropertySource<?> propertySource = new OriginCapablePropertySource<>(
|
||||
new MapPropertySource("test", source));
|
||||
TestPropertyMapper mapper = new TestPropertyMapper();
|
||||
ConfigurationPropertyName name = ConfigurationPropertyName.of("my.key");
|
||||
mapper.addFromConfigurationProperty(name, "key");
|
||||
PropertySourceIterableConfigurationPropertySource adapter = new PropertySourceIterableConfigurationPropertySource(
|
||||
propertySource, mapper);
|
||||
assertThat(adapter.getConfigurationProperty(name).getOrigin().toString())
|
||||
.isEqualTo("TestOrigin key");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link PropertySource} that's also a {@link OriginLookup}.
|
||||
*/
|
||||
private static class OriginCapablePropertySource<T>
|
||||
extends EnumerablePropertySource<T> implements OriginLookup<String> {
|
||||
|
||||
private final EnumerablePropertySource<T> propertySource;
|
||||
|
||||
OriginCapablePropertySource(EnumerablePropertySource<T> propertySource) {
|
||||
super(propertySource.getName(), propertySource.getSource());
|
||||
this.propertySource = propertySource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
return this.propertySource.getProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
return this.propertySource.getPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Origin getOrigin(String name) {
|
||||
return new Origin() {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestOrigin " + name;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Test {@link PropertyMapper} implementation.
|
||||
*/
|
||||
class TestPropertyMapper implements PropertyMapper {
|
||||
|
||||
private MultiValueMap<String, PropertyMapping> fromSource = new LinkedMultiValueMap<>();
|
||||
|
||||
private MultiValueMap<ConfigurationPropertyName, PropertyMapping> fromConfig = new LinkedMultiValueMap<>();
|
||||
|
||||
public void addFromProperySource(String from, String... to) {
|
||||
for (String configurationPropertyName : to) {
|
||||
this.fromSource.add(from, new PropertyMapping(from,
|
||||
ConfigurationPropertyName.of(configurationPropertyName)));
|
||||
}
|
||||
}
|
||||
|
||||
public void addFromConfigurationProperty(ConfigurationPropertyName from,
|
||||
String... to) {
|
||||
for (String propertySourceName : to) {
|
||||
this.fromConfig.add(from, new PropertyMapping(propertySourceName, from));
|
||||
}
|
||||
}
|
||||
|
||||
public void addFromConfigurationProperty(ConfigurationPropertyName from, String to,
|
||||
Function<Object, Object> extractor) {
|
||||
this.fromConfig.add(from, new PropertyMapping(to, from, extractor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PropertyMapping> map(PropertySource<?> propertySource,
|
||||
String propertySourceName) {
|
||||
return this.fromSource.getOrDefault(propertySourceName, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PropertyMapping> map(PropertySource<?> propertySource,
|
||||
ConfigurationPropertyName configurationPropertyName) {
|
||||
return this.fromConfig.getOrDefault(configurationPropertyName,
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue