Browse Source
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
27 changed files with 968 additions and 398 deletions
@ -0,0 +1,60 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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