This is an automated email from the ASF dual-hosted git repository. anatole pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-tamaya.git
commit 6ab216df0ec6081cd35c1847048b6dab93d1b3bf Author: Anatole Tresch <[email protected]> AuthorDate: Tue Dec 11 11:00:37 2018 +0100 TAMAYA-372: Clarified default metadata format. --- .../apache/tamaya/spi/ConfigurationBuilder.java | 7 +- .../apache/tamaya/spi/ConfigurationContext.java | 7 +- .../org/apache/tamaya/spi/ConversionContext.java | 23 ++-- .../java/org/apache/tamaya/spi/FilterContext.java | 41 ++++--- .../java/org/apache/tamaya/spi/ObjectValue.java | 4 +- .../java/org/apache/tamaya/spi/PropertyValue.java | 9 +- .../java/org/apache/tamaya/ConfigurationTest.java | 69 +++++++++++- .../java/org/apache/tamaya/InvocationRecorder.java | 76 +++++++++++++ .../java/org/apache/tamaya/TestConfiguration.java | 25 +++-- .../apache/tamaya/TestConfigurationProvider.java | 4 +- .../apache/tamaya/spi/ConversionContextTest.java | 54 +++++++++ .../org/apache/tamaya/spi/FilterContextTest.java | 10 ++ .../org/apache/tamaya/spi/PropertyValueTest.java | 125 ++++++++++++++++++++- .../spisupport/DefaultConfigurationBuilder.java | 8 +- .../spisupport/DefaultConfigurationContext.java | 4 +- .../tamaya/spisupport/DefaultMetaDataProvider.java | 86 ++++++++++---- .../apache/tamaya/spisupport/MetadataProvider.java | 27 +++-- .../tamaya/spisupport/PropertyFiltering.java | 3 +- .../spisupport/MockedConfigurationContext.java | 2 +- 19 files changed, 491 insertions(+), 93 deletions(-) diff --git a/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationBuilder.java b/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationBuilder.java index b4d67ce..0e2d95f 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationBuilder.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationBuilder.java @@ -105,18 +105,21 @@ public interface ConfigurationBuilder { /** * Adds (overrides existing value) the given sources as property sources. + * + * @param property the property key, not null, * @param key the key, not null. * @param value the value, not null. * @return the current configuration builder. */ - ConfigurationBuilder setMeta(String key, String value); + ConfigurationBuilder setMeta(String property, String key, String value); /** * Adds (overrides existing value with same same keys) the given sources as property sources. + * @param property the property key, not null, * @param metaData the metadata, not null. * @return the current configuration builder. */ - ConfigurationBuilder setMeta(Map<String, String> metaData); + ConfigurationBuilder setMeta(String property, Map<String, String> metaData); /** * This method can be used for programmatically adding {@link PropertySource}s. diff --git a/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java b/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java index 86c3831..e762576 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/ConfigurationContext.java @@ -34,9 +34,10 @@ public interface ConfigurationContext { /** * Get the metadata evaluated for this configuration. - * @return the metadata accessor, never null. + * @param key the property key, not null. + * @return the metadata fpr this key, never null. */ - Map<String,String> getMetaData(); + Map<String,String> getMetaData(String key); /** * Access the underlying {@link ServiceContext}. @@ -149,7 +150,7 @@ public interface ConfigurationContext { */ ConfigurationContext EMPTY = new ConfigurationContext() { @Override - public Map<String,String> getMetaData() { + public Map<String,String> getMetaData(String key) { return Collections.emptyMap(); } diff --git a/code/api/src/main/java/org/apache/tamaya/spi/ConversionContext.java b/code/api/src/main/java/org/apache/tamaya/spi/ConversionContext.java index ff321dc..cb4ae32 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/ConversionContext.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/ConversionContext.java @@ -99,20 +99,18 @@ public class ConversionContext { /** - * Evaluate the current metadata from the given values. Later values hereby are more significant. + * Evaluate the metadata for the current target key from the given values. Later values hereby are more significant. * @return the evaluated meta data map. */ public Map<String, String> getMeta() { Map<String, String> metaMap = new HashMap<>(); - if(values.size()>0){ - String baseKey = values.get(0).getQualifiedKey()+"."; - - values.forEach(val -> this.getConfiguration().getContext().getMetaData().entrySet().forEach( - en -> { - if(en.getKey().startsWith(baseKey)) { - metaMap.put(en.getKey().substring(baseKey.length()), en.getValue()); - } - })); + if(configuration!=null){ + metaMap.putAll(configuration.getContext().getMetaData(key)); + } + for(PropertyValue val:values){ + if(key.equals(val.getQualifiedKey())){ + metaMap.putAll(val.getMeta()); + } } return metaMap; } @@ -161,7 +159,10 @@ public class ConversionContext { */ @Deprecated public ConfigurationContext getConfigurationContext() { - return getConfiguration().getContext(); + if(configuration!=null) { + return configuration.getContext(); + } + return ConfigurationContext.EMPTY; } /** diff --git a/code/api/src/main/java/org/apache/tamaya/spi/FilterContext.java b/code/api/src/main/java/org/apache/tamaya/spi/FilterContext.java index e120744..cdf8652 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/FilterContext.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/FilterContext.java @@ -21,18 +21,17 @@ package org.apache.tamaya.spi; import org.apache.tamaya.Configuration; import java.util.*; -import java.util.function.Consumer; /** - * A filter context containing all the required values for implementing filtering. + * A filter configurationContext containing all the required values for implementing filtering. * * @see PropertyFilter */ public class FilterContext { /** The key. */ private final List<PropertyValue> values; - /** The current context. */ - private final ConfigurationContext context; + /** The current configurationContext. */ + private final ConfigurationContext configurationContext; @Experimental private Map<String, PropertyValue> configEntries = new HashMap<>(); @Experimental @@ -45,17 +44,17 @@ public class FilterContext { * * @param value the createValue under evaluation, not {@code null}. * @param configEntries the raw configuration data available in the - * current evaluation context, not {@code null}. - * @param context the current context, not {@code null}. + * current evaluation configurationContext, not {@code null}. + * @param configurationContext the current configurationContext, not {@code null}. */ - public FilterContext(PropertyValue value, Map<String,PropertyValue> configEntries, ConfigurationContext context) { + public FilterContext(PropertyValue value, Map<String,PropertyValue> configEntries, ConfigurationContext configurationContext) { Objects.requireNonNull(value, "Value must not be null."); Objects.requireNonNull(configEntries, "Initial configuration entries must be not null."); - Objects.requireNonNull(context, "Context must be not null."); + Objects.requireNonNull(configurationContext, "Context must be not null."); this.singlePropertyScoped = false; this.values = Collections.singletonList(Objects.requireNonNull(value)); - this.context = Objects.requireNonNull(context); + this.configurationContext = Objects.requireNonNull(configurationContext); this.configEntries.putAll(configEntries); this.configEntries = Collections.unmodifiableMap(this.configEntries); } @@ -64,14 +63,14 @@ public class FilterContext { * Creates a new FilterContext, for filtering of a single createValue access * using {@link Configuration#getProperties()}. * @param value the createValue under evaluation, not {@code null}. - * @param context the current context, not {@code null}. + * @param configurationContext the current configurationContext, not {@code null}. */ - public FilterContext(PropertyValue value, ConfigurationContext context) { + public FilterContext(PropertyValue value, ConfigurationContext configurationContext) { Objects.requireNonNull(value, "Value must not be null."); - Objects.requireNonNull(context, "Context must be not null."); + Objects.requireNonNull(configurationContext, "Context must be not null."); this.singlePropertyScoped = true; - this.context = Objects.requireNonNull(context); + this.configurationContext = Objects.requireNonNull(configurationContext); this.values = Collections.singletonList(Objects.requireNonNull(value)); this.configEntries = Collections.unmodifiableMap(this.configEntries); } @@ -80,24 +79,24 @@ public class FilterContext { * Creates a new FilterContext, for filtering of a single createValue access * using {@link Configuration#getProperties()}. * @param values the createValue under evaluation, not {@code null}. - * @param context the current context, not {@code null}. + * @param configurationContext the current configurationContext, not {@code null}. */ - public FilterContext(List<PropertyValue> values, ConfigurationContext context) { + public FilterContext(List<PropertyValue> values, ConfigurationContext configurationContext) { Objects.requireNonNull(values, "Value must not be null."); - Objects.requireNonNull(context, "Context must be not null."); + Objects.requireNonNull(configurationContext, "Context must be not null."); this.singlePropertyScoped = true; - this.context = Objects.requireNonNull(context); + this.configurationContext = Objects.requireNonNull(configurationContext); this.values = Collections.unmodifiableList(new ArrayList<>(values)); this.configEntries = Collections.unmodifiableMap(this.configEntries); } /** - * Get the current context. - * @return the current context, not {@code null}. + * Get the current configurationContext. + * @return the current configurationContext, not {@code null}. */ - public ConfigurationContext current(){ - return context; + public ConfigurationContext getConfigurationContext(){ + return configurationContext; } /** diff --git a/code/api/src/main/java/org/apache/tamaya/spi/ObjectValue.java b/code/api/src/main/java/org/apache/tamaya/spi/ObjectValue.java index f7f6330..7d09eee 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/ObjectValue.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/ObjectValue.java @@ -56,7 +56,7 @@ public final class ObjectValue extends PropertyValue{ * @return the createValue type, never null. */ public ValueType getValueType() { - return ValueType.OBJECT; + return ValueType.MAP; } /** @@ -305,7 +305,7 @@ public final class ObjectValue extends PropertyValue{ @Override public String toString() { - return "PropertyValue[OBJECT]{" + + return "PropertyValue[MAP]{" + '\'' +getQualifiedKey() + '\'' + (getValue()!=null?", createValue='" + getValue() + '\'':"") + ", size='" + getSize() + '\'' + diff --git a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java index 9381f5e..048e4b2 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java @@ -53,7 +53,7 @@ public class PropertyValue implements Serializable, Iterable<PropertyValue>{ */ public enum ValueType{ /** A multi valued property value, which contains named child properties. */ - OBJECT, + MAP, /** A multi valued property value, which contains unnamed child properties. */ ARRAY, /** A simple value property. */ @@ -74,7 +74,7 @@ public class PropertyValue implements Serializable, Iterable<PropertyValue>{ Objects.requireNonNull(key, "Key must be given."); Objects.requireNonNull(source, "Source must be given"); - return new PropertyValueBuilder(key, source); + return new PropertyValueBuilder(key, null).setSource(source); } /** @@ -113,7 +113,7 @@ public class PropertyValue implements Serializable, Iterable<PropertyValue>{ } /** - * Creates a new createValue of type {@link ValueType#OBJECT}. + * Creates a new createValue of type {@link ValueType#MAP}. * @param key the key, not {@code null}. * @return a new createValue instance. */ @@ -569,8 +569,9 @@ public class PropertyValue implements Serializable, Iterable<PropertyValue>{ * Sets the new parent, used iternally when converting between value types. * @param parent the parent value. */ - protected final void setParent(PropertyValue parent){ + protected PropertyValue setParent(PropertyValue parent){ this.parent = parent; + return this; } diff --git a/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java b/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java index 58811b4..d950c01 100644 --- a/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java +++ b/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java @@ -19,9 +19,12 @@ package org.apache.tamaya; import org.apache.tamaya.spi.ConfigurationBuilder; +import org.apache.tamaya.spi.ConfigurationContext; import org.junit.Test; import org.mockito.Mockito; +import java.math.BigDecimal; +import java.util.Arrays; import java.util.function.Function; import java.util.function.UnaryOperator; @@ -36,6 +39,26 @@ import static org.assertj.core.api.Assertions.*; public class ConfigurationTest { @Test + public void test_current() throws Exception { + assertThat(Configuration.current()).isNotNull(); + } + + @Test + public void test_current_classloader() throws Exception { + assertThat(Configuration.current(ClassLoader.getSystemClassLoader())).isNotNull(); + } + + @Test + public void test_release() throws Exception { + Configuration c1 = Configuration.current(); + Configuration c2 = Configuration.current(); + Configuration.releaseConfiguration(c2.getContext().getServiceContext().getClassLoader()); + Configuration c3 = Configuration.current(); + assertThat(c1).isSameAs(c2); + assertThat(c2).isNotSameAs(c3); + } + + @Test public void testget() throws Exception { assertThat(Boolean.TRUE).isEqualTo(Configuration.current().get("booleanTrue", Boolean.class)); assertThat(Boolean.FALSE).isEqualTo(Configuration.current().get("booleanFalse", Boolean.class)); @@ -48,10 +71,33 @@ public class ConfigurationTest { } @Test + public void testget_Iterable() throws Exception { + assertThat(Configuration.current().get(Arrays.asList("String","foo"))).isEqualTo("aStringValue"); + assertThat(Configuration.current().get(Arrays.asList("foo", "bar", "String"))).isEqualTo("aStringValue"); + } + + @Test + public void testget_Iterable_default() throws Exception { + assertThat("foo").isEqualTo(Configuration.current().getOrDefault(Arrays.asList("adasd","safsada"), "foo")); + assertThat("aStringValue").isEqualTo(Configuration.current().getOrDefault(Arrays.asList("foo1", "bar1", "String"), "foo")); + } + + @Test + public void testget_Iterable_default_typed() throws Exception { + assertThat(25).isEqualTo(Configuration.current().getOrDefault(Arrays.asList("adasd","safsada"),Integer.class,25)); + assertThat(BigDecimal.ZERO).isEqualTo(Configuration.current().getOrDefault(Arrays.asList("foo1", "bar1", "2.0"), BigDecimal.class, BigDecimal.ZERO)); + } + + @Test + public void testget_Iterable_with_Type() throws Exception { + assertThat(Boolean.TRUE).isEqualTo(Configuration.current().get(Arrays.asList("booleanTrue","booleanFalse"), Boolean.class)); + assertThat(Boolean.TRUE).isEqualTo(Configuration.current().get(Arrays.asList("foo1", "bar1", "booleanTrue"), Boolean.class)); + } + + @Test public void testGetBoolean() throws Exception { assertThat(Configuration.current().get("booleanTrue", Boolean.class)).isTrue(); assertThat(Configuration.current().get("booleanFalse", Boolean.class)).isFalse(); - assertThat(Configuration.current().get("foorBar", Boolean.class)).isFalse(); } @Test @@ -133,4 +179,25 @@ public class ConfigurationTest { ConfigurationBuilder result = Configuration.createConfigurationBuilder(); assertThat(result instanceof ConfigurationBuilder).isTrue(); } + + @Test + public void testEmpty() { + Configuration c = Configuration.EMPTY; + assertThat(c).isNotNull(); + assertThat(c.get("foo")).isNull(); + assertThat(c.get("foo", Boolean.class)).isNull(); + assertThat(c.get(Arrays.asList("foo", "bar"))).isNull(); + assertThat(c.getOrDefault("foo", "")).isEqualTo(""); + assertThat(c.getOrDefault("foo", Integer.class, 234)).isEqualTo(234); + assertThat(c.getOrDefault(Arrays.asList("foo", "bar"), "default")).isEqualTo("default"); + assertThat(c.getOrDefault(Arrays.asList("foo", "bar"), Integer.class, 234)).isEqualTo(234); + assertThat(c.get(Arrays.asList("foo", "bar"), Integer.class)).isNull(); + assertThat(c.getContext()).isEqualTo(ConfigurationContext.EMPTY); + assertThat(c.getOptional("kjhkjh")).isNotNull(); + assertThat(c.getOptional("kjhkjh")).isNotPresent(); + assertThat(c.getOptional("kjhkjh", Integer.class)).isNotNull(); + assertThat(c.getOptional("kjhkjh", Integer.class)).isNotPresent(); + assertThat(c.get("foo")).isNull(); + assertThat(c.get("foo", Boolean.class)).isNull(); + } } diff --git a/code/api/src/test/java/org/apache/tamaya/InvocationRecorder.java b/code/api/src/test/java/org/apache/tamaya/InvocationRecorder.java new file mode 100644 index 0000000..a3ebdc7 --- /dev/null +++ b/code/api/src/test/java/org/apache/tamaya/InvocationRecorder.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tamaya; + + +import org.junit.Assert; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class InvocationRecorder { + + private List<Invocation> invocations = new ArrayList<>(); + + private Object record(Object instance, Method method, Object[] args) throws Throwable { + Invocation invocation = new Invocation(method.getName(), args); + this.invocations.add(invocation); + return method.invoke(instance, args); + } + + public <T> T createProxy(Object instance, Class<T>... types) { + return (T) Proxy.newProxyInstance( + getClass().getClassLoader(), types, + (proxy,method,params) -> this.record(instance, method, params)); + } + + public void recordMethodCall(Object... params) { + Exception e = new Exception(); + String methodName = e.getStackTrace()[1].getMethodName(); + invocations.add(new Invocation(methodName, params)); + } + + public static final class Invocation{ + public String methodName; + public Object[] params; + + public Invocation(String methodName, Object[] params) { + this.methodName = methodName; + this.params = params; + } + } + + public List<Invocation> getInvocations(){ + return invocations; + } + + public void assertInvocation(String method, Object... params){ + for(Invocation invocation:invocations){ + if(invocation.methodName.equals(method)){ + if(Arrays.equals(invocation.params, params)){ + return; + } + } + } + Assert.fail("No such invocation: "+method + Arrays.toString(params)); + } +} diff --git a/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java b/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java index cd6eeaf..d06753a 100644 --- a/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java +++ b/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java @@ -49,9 +49,7 @@ public class TestConfiguration implements Configuration { VALUES.put("String", "aStringValue"); } - @SuppressWarnings("unchecked") - @Override - public <T> T get(String key, TypeLiteral<T> type) { + private <T> T getInternal(String key, TypeLiteral<T> type) { if (type.getRawType().equals(Long.class)) { return (T) VALUES.get(key); } else if (type.getRawType().equals(Integer.class)) { @@ -67,18 +65,31 @@ public class TestConfiguration implements Configuration { } else if (type.getRawType().equals(Boolean.class)) { if ("booleanTrue".equals(key)) { return (T) Boolean.TRUE; - } else { + } else if ("booleanFalse".equals(key)) { return (T) Boolean.FALSE; } } else if (type.getRawType().equals(String.class)) { - return (T) VALUES.get(key); + Object value = VALUES.get(key); + if(value!=null){ + return (T)String.valueOf(value); + } + } + return null; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T get(String key, TypeLiteral<T> type) { + T t = getInternal(key, type); + if(t==null) { + throw new ConfigException("No such property: " + key); } - throw new ConfigException("No such property: " + key); + return t; } @Override public <T> T getOrDefault(String key, TypeLiteral<T> type, T defaultValue) { - T val = get(key, type); + T val = getInternal(key, type); if (val == null) { return defaultValue; } diff --git a/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java b/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java index e5622e4..fae3c53 100644 --- a/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java +++ b/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java @@ -57,6 +57,8 @@ public class TestConfigurationProvider implements ConfigurationProviderSpi { @Override public Configuration releaseConfiguration(ClassLoader classloader) { - return null; + Configuration prev = config; + config = new TestConfiguration(); + return prev; } } diff --git a/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java b/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java index 620b2b6..7e6110e 100644 --- a/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java +++ b/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java @@ -26,9 +26,11 @@ import org.junit.Test; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.*; +import static org.junit.Assert.*; /** * Tests for {@link ConversionContext}, created by atsticks on 20.08.16. @@ -97,6 +99,58 @@ public class ConversionContextTest { assertThat(ctx.toString()).isEqualTo("ConversionContext{configuration=null, key='toString', targetType=TypeLiteral{type=interface java.util.List}, annotatedElement=null, supportedFormats=[0.0.0.0/nnn (MyConverter), x.x.x.x/yyy (MyConverter)]}"); } + @Test + public void testGetSetValues_Ellipse(){ + ConversionContext ctx = new ConversionContext.Builder("toString", TypeLiteral.of(List.class)) + .addSupportedFormats(MyConverter.class, "0.0.0.0/nnn", "x.x.x.x/yyy") + .setValues(PropertyValue.createValue("test", "value")).build(); + assertNotNull(ctx.getValues()); + assertEquals(ctx.getValues().size(), 1); + assertEquals("value", ctx.getValues().get(0).getValue()); + assertEquals("test", ctx.getValues().get(0).getKey()); + } + + @Test + public void testGetSetValues_List(){ + ConversionContext ctx = new ConversionContext.Builder("toString", TypeLiteral.of(List.class)) + .addSupportedFormats(MyConverter.class, "0.0.0.0/nnn", "x.x.x.x/yyy") + .setValues(Collections.singletonList(PropertyValue.createValue("test", "value"))).build(); + assertNotNull(ctx.getValues()); + assertEquals(ctx.getValues().size(), 1); + assertEquals("value", ctx.getValues().get(0).getValue()); + assertEquals("test", ctx.getValues().get(0).getKey()); + } + + @Test + public void testGetConfigurationContext(){ + ConversionContext ctx = new ConversionContext.Builder("toString", TypeLiteral.of(List.class)) + .addSupportedFormats(MyConverter.class, "0.0.0.0/nnn", "x.x.x.x/yyy") + .setValues(PropertyValue.createValue("test", "value")).build(); + assertNotNull(ctx.getConfigurationContext()); + } + + @Test + public void testGetMeta(){ + ConversionContext ctx = new ConversionContext.Builder("test", TypeLiteral.of(List.class)) + .addSupportedFormats(MyConverter.class, "0.0.0.0/nnn", "x.x.x.x/yyy") + .setValues(PropertyValue.createValue("test", "value") + .setMeta("meta1", "val1").setMeta("meta2", "val2")).build(); + assertNotNull(ctx.getMeta()); + assertFalse(ctx.getMeta().isEmpty()); + assertEquals(2, ctx.getMeta().size()); + } + + @Test + public void testBuilderToString() { + ConversionContext.Builder b = new ConversionContext.Builder("toString", TypeLiteral.of(List.class)) + .addSupportedFormats(MyConverter.class, "0.0.0.0/nnn", "x.x.x.x/yyy"); + assertNotNull(b.toString()); + assertTrue(b.toString().contains("targetType=TypeLiteral{type=interface java.util.List}")); + assertTrue(b.toString().contains("supportedFormats=[0.0.0.0/nnn (MyConverter), x.x.x.x/yyy (MyConverter)]")); + assertTrue(b.toString().contains("annotatedElement")); + assertTrue(b.toString().contains("key='toString'")); + assertTrue(b.toString().contains("Builder")); + } private static final AnnotatedElement MyAnnotatedElement = new AnnotatedElement() { @Override diff --git a/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java b/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java index 1b74f0b..24fed66 100644 --- a/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java +++ b/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java @@ -37,6 +37,16 @@ import static org.junit.Assert.assertNotNull; */ public class FilterContextTest { + @Test + public void constructorWithContext() { + PropertyValue val = new PropertyValue(null, ""); + FilterContext ctx = new FilterContext(val, ConfigurationContext.EMPTY); + assertEquals(val, ctx.getProperty()); + assertEquals(ConfigurationContext.EMPTY, ctx.getConfigurationContext()); + assertNotNull(ctx.getConfigEntries()); + assertEquals(1, ctx.getAllValues().size()); + } + // @Test // public void setNullContext() { // FilterContext.set(null); diff --git a/code/api/src/test/java/org/apache/tamaya/spi/PropertyValueTest.java b/code/api/src/test/java/org/apache/tamaya/spi/PropertyValueTest.java index 035c458..6317714 100644 --- a/code/api/src/test/java/org/apache/tamaya/spi/PropertyValueTest.java +++ b/code/api/src/test/java/org/apache/tamaya/spi/PropertyValueTest.java @@ -43,6 +43,14 @@ public class PropertyValueTest { } @Test + public void builder() throws Exception { + PropertyValueBuilder b = PropertyValue.builder("a", "b"); + assertNotNull(b); + assertEquals("a", b.key); + assertEquals("b", b.source); + } + + @Test public void testOf(){ assertThat(PropertyValue.of("k", "v", "testGetKey")).isNotNull(); } @@ -116,6 +124,41 @@ public class PropertyValueTest { } @Test + public void testMap() throws Exception { + Map<String, String> map = new HashMap<>(); + map.put("a", "b"); + map.put("b", "c"); + Map<String, PropertyValue> result = PropertyValue.map(map, "source"); + assertNotNull(result); + assertEquals(map.size(), result.size()); + for(PropertyValue pv:result.values()){ + assertEquals("source", pv.getMetaEntry("source")); + } + assertEquals("b", map.get("a")); + assertEquals("c", map.get("b")); + } + + @Test + public void testMap_WithMeta() throws Exception { + Map<String, String> map = new HashMap<>(); + map.put("a", "b"); + map.put("b", "c"); + Map<String, String> meta = new HashMap<>(); + meta.put("m1", "m1v"); + meta.put("m2", "m2v"); + Map<String, PropertyValue> result = PropertyValue.map(map, "source", meta); + assertNotNull(result); + assertEquals(map.size(), result.size()); + for(PropertyValue pv:result.values()){ + assertEquals("source", pv.getMetaEntry("source")); + assertEquals("m1v", pv.getMeta("m1")); + assertEquals("m2v", pv.getMeta("m2")); + } + assertEquals("b", map.get("a")); + assertEquals("c", map.get("b")); + } + + @Test public void testHashCode(){ assertThat(PropertyValue.of("k", "v", "testGetKey").hashCode()).isEqualTo(PropertyValue.of("k", "v", "testGetKey").hashCode()); assertThat(PropertyValue.of("k", "v", "testGetKey").hashCode()).isNotSameAs(PropertyValue.of("k1", "v", "testGetKey").hashCode()); @@ -260,6 +303,39 @@ public class PropertyValueTest { } @Test + public void isImmutable() { + PropertyValue n = PropertyValue.createValue("", ""); + assertFalse(n.isImmutable()); + n.immutable(); + assertTrue(n.isImmutable()); + assertFalse(n.mutable().isImmutable()); + } + + @Test + public void isRoot() { + PropertyValue n = PropertyValue.createValue("", ""); + assertTrue(n.isRoot()); + n = PropertyValue.createValue("", "").setParent(n); + assertFalse(n.isRoot()); + } + + @Test(expected=IllegalStateException.class) + public void checkImmutableChangeThrowsExceotion() { + PropertyValue n = PropertyValue.createValue("", ""); + n.immutable(); + n.setValue("jhgjg"); + } + + @Test + public void checkMutable() { + PropertyValue n = PropertyValue.createValue("", ""); + n.immutable(); + n = n.mutable(); + n.setValue("jhgjg"); + assertEquals("jhgjg", n.getValue()); + } + + @Test public void getParent() { ObjectValue n = PropertyValue.createObject(""); assertNull(n.getParent()); @@ -268,6 +344,53 @@ public class PropertyValueTest { assertNotNull(n.getField("b").getParent()); } + @Test + public void size(){ + PropertyValue n = PropertyValue.createValue("key", ""); + assertEquals(0, n.getSize()); + assertFalse(n.iterator().hasNext()); + } + + @Test + public void setValue() { + PropertyValue n = PropertyValue.createValue("key", ""); + assertEquals("", n.getValue()); + n.setValue("jhgjg"); + assertEquals("jhgjg", n.getValue()); + } + + @Test + public void setKey() { + PropertyValue n = PropertyValue.createValue("key", ""); + assertEquals("key", n.getKey()); + n.setKey("jhgjg"); + assertEquals("jhgjg", n.getKey()); + } + + @Test + public void toBuilder() { + PropertyValue n = PropertyValue.createValue("key", ""); + assertNotNull(n.toBuilder()); + } + + @Test + public void toPropertyValue() { + PropertyValue n = PropertyValue.createValue("key", ""); + assertTrue(n == n.toPropertyValue()); + } + + @Test + public void toObjectValue() { + PropertyValue n = PropertyValue.createValue("key", ""); + assertNotNull(n.toObjectValue()); + } + + @Test + public void toListValue() { + PropertyValue n = PropertyValue.createValue("key", ""); + assertNotNull(n.toListValue()); + } + // @Test // public void getChildren_Filtered() { // PropertyValue n = PropertyValue.createObject(); @@ -345,7 +468,7 @@ public class PropertyValueTest { n.setField("a", "aVal"); n.setField("b.b2.b3", "b3Val"); n.setFieldList("c").addValue("cVal1"); - assertEquals("PropertyValue[OBJECT]{'', size='3'}", n.toString()); + assertEquals("PropertyValue[MAP]{'', size='3'}", n.toString()); } } \ No newline at end of file diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java index 1276daa..95c4d50 100644 --- a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java +++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationBuilder.java @@ -122,14 +122,14 @@ public class DefaultConfigurationBuilder implements ConfigurationBuilder { } @Override - public ConfigurationBuilder setMeta(String key, String value){ - this.metaDataProvider.setMeta(key, value); + public ConfigurationBuilder setMeta(String property, String key, String value){ + this.metaDataProvider.setMeta(property, key, value); return this; } @Override - public ConfigurationBuilder setMeta(Map<String, String> metaData){ - this.metaDataProvider.setMeta(metaData); + public ConfigurationBuilder setMeta(String property, Map<String, String> metaData){ + this.metaDataProvider.setMeta(property, metaData); return this; } diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationContext.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationContext.java index a6d9612..1c58419 100644 --- a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationContext.java +++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultConfigurationContext.java @@ -103,8 +103,8 @@ public class DefaultConfigurationContext implements ConfigurationContext { @Override - public Map<String,String> getMetaData() { - return metaDataProvider.getMetaData(); + public Map<String,String> getMetaData(String key) { + return metaDataProvider.getMetaData(key); } @Override diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultMetaDataProvider.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultMetaDataProvider.java index cc43360..c1c5be1 100644 --- a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultMetaDataProvider.java +++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/DefaultMetaDataProvider.java @@ -25,19 +25,21 @@ import org.apache.tamaya.spi.PropertyValue; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Logger; /** * Default metadata provider implementation, which searches for all kind of entries - * starting with {@code [META]}. All matching key/values are added to the - * meta data map, hereby reoving the key prefix. + * formatted as {@code [(META)key].metaKey=metaValue}. All matching key/values are added to the + * meta data map for the given key as {@code metaKey=metaValue} meta entries. */ public class DefaultMetaDataProvider implements MetadataProvider { - private static final String META_PREFIX = "[META]"; + private static final Logger LOG = Logger.getLogger(DefaultMetaDataProvider.class.getName()); + private static final String META_PREFIX = "[(META)"; private ConfigurationContext context; - private Map<String, String> additionalProperties = new ConcurrentHashMap<>(); + private Map<String, Map<String, String>> additionalProperties = new ConcurrentHashMap<>(); private AtomicLong lastHash = new AtomicLong(); - private Map<String, String> propertyCache = new HashMap<>(); + private Map<String, Map<String, String>> propertyCache = new HashMap<>(); @Override public MetadataProvider init(ConfigurationContext context) { @@ -46,50 +48,88 @@ public class DefaultMetaDataProvider implements MetadataProvider { } @Override - public Map<String, String> getMetaData() { + public Map<String, String> getMetaData(String property) { long configHash = Objects.hash(context.getPropertySources().toArray()); if(configHash!=lastHash.get()){ lastHash.set(configHash); propertyCache = loadMetaProperties(); } - return propertyCache; + Map<String, String> meta = propertyCache.get(property); + if(meta==null){ + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(meta); } - private Map<String, String> loadMetaProperties() { - Map<String, String> result = new HashMap<>(); + private Map<String, Map<String, String> > loadMetaProperties() { + Map<String, Map<String, String> > result = new HashMap<>(); for(PropertySource ps:context.getPropertySources()){ ps.getProperties().values().forEach(v -> { - loadMetaData(v, result); + Map<String, String> meta = result.computeIfAbsent(v.getKey(), k -> new HashMap<>()); + meta.putAll(v.getMeta()); + if(v.getQualifiedKey().toUpperCase(Locale.ENGLISH).startsWith(META_PREFIX)){ + loadExplicitMetadata(v); + } }); } - result.putAll(additionalProperties); + // Override with manual properties + for(Map.Entry<String,Map<String, String>> en: additionalProperties.entrySet()) { + Map<String, String> meta = result.get(en.getKey()); + meta.putAll(en.getValue()); + } return Collections.unmodifiableMap(result); } /** - * Iterates all values and it's children and adds all meta-entries found. - * @param value the starting value. - * @param result the result map to add/override values found. + * Iterates all values and it's children and adds all meta-entries found in the configuration + * (entries starting with {@code [(META)}). */ - private void loadMetaData(PropertyValue value, Map<String, String> result) { + private void loadExplicitMetadata(PropertyValue value) { String key = value.getQualifiedKey(); - if(key.toUpperCase(Locale.ENGLISH).startsWith(META_PREFIX)){ - if(value.getValue()!=null){ - result.put(key.substring(META_PREFIX.length()), value.getValue()); + if(value.getValue()!=null){ + String[] keyValue = getMetaKeys(key); + if(keyValue==null){ + LOG.warning("Encountered invalid META-ENTRY: " + key); + }else { + Map<String, String> meta = additionalProperties.computeIfAbsent(keyValue[0], k -> new HashMap<>()); + meta.put(keyValue[1], value.getValue()); } - value.iterator().forEachRemaining(v -> loadMetaData(v, result)); } } + private String[] getMetaKeys(String fullKey) { + String strippedKey = fullKey.substring(META_PREFIX.length()); + int index = strippedKey.lastIndexOf(']'); + if(index<0){ + // invalid meta key + return null; + } + String[] result = new String[2]; + result[0] = strippedKey.substring(0,index); + result[1] = strippedKey.substring(index+1); + if(result[1].startsWith(".")){ + result[1] = result[1].substring(1); + } + return result; + } + + @Override + public MetadataProvider setMeta(String property, String key, String value) { + additionalProperties.computeIfAbsent(property, p -> new HashMap<>()) + .put(key, value); + return this; + } + @Override - public MetadataProvider setMeta(String key, String value) { - additionalProperties.put(key, value); + public MetadataProvider setMeta(String property, Map<String, String> metaData) { + additionalProperties.computeIfAbsent(property, p -> new HashMap<>()) + .putAll(metaData); return this; } @Override - public MetadataProvider setMeta(Map<String, String> metaData) { - additionalProperties.putAll(metaData); + public MetadataProvider reset(String property) { + additionalProperties.remove(property); return this; } diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/MetadataProvider.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/MetadataProvider.java index b5040fa..9f80dec 100644 --- a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/MetadataProvider.java +++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/MetadataProvider.java @@ -25,7 +25,7 @@ import java.util.Map; /** * This interface allows to plugin different metadata mechanism. The default implementation * does load metadata information along the same property sources hierarchy as configuration. - * MetaData entries are identified by a {@code [META]} prefix. Alternate implementations can + * MetaData entries are in the format {@code [(META)key].metakey=metavalue}. Alternate implementations can * choose whatever is appropriate, including loading metadata from external sources. */ public interface MetadataProvider { @@ -39,31 +39,42 @@ public interface MetadataProvider { MetadataProvider init(ConfigurationContext context); /** - * Access the current metadata for the given configuration context. The MetaData will be - * accessible from {@link ConfigurationContext#getMetaData()}. Note that the metadata must not + * Access the current metadata for the given configuration context and key. The MetaData will be + * accessible from {@link ConfigurationContext#getMetaData(String)}. Note that the metadata must not * to be cached by it's consumers, so caching/optimazitation is delegated to this implementation. + * @param key the property key, not null. * @return the (immutable) metadata of this configuration context. */ - Map<String,String> getMetaData(); + Map<String,String> getMetaData(String key); /** * Adds additional metadata. This metadata entries typically override all entries * from alternate sources. * - * @param key the key, not null. - * @param value the value, not null. + * @param property the property key, not null. + * @param key the metadata key, not null. + * @param value the metadata value, not null. * @return this instance, for chaining. */ - MetadataProvider setMeta(String key, String value); + MetadataProvider setMeta(String property, String key, String value); /** * Adds additional metadata. This metadata entries typically override all entries * from alternate sources. * + * @param property the property key, not null. * @param metaData the metadata to set/replace. * @return this instance, for chaining. */ - MetadataProvider setMeta(Map<String, String> metaData); + MetadataProvider setMeta(String property, Map<String, String> metaData); + + /** + * Resets metadata for a property, which means it reloads metadata based on the given context and + * + * param property the property key, not null. + * @return this instance, for chaining. + */ + MetadataProvider reset(String property); /** * Resets this instance, which means it reloads metadata based on the given context and diff --git a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java index 8df6a21..c6943bf 100644 --- a/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java +++ b/code/spi-support/src/main/java/org/apache/tamaya/spisupport/PropertyFiltering.java @@ -21,7 +21,6 @@ package org.apache.tamaya.spisupport; import org.apache.tamaya.spi.*; import java.util.*; -import java.util.logging.Filter; import java.util.logging.Level; import java.util.logging.Logger; @@ -103,7 +102,7 @@ public final class PropertyFiltering{ for (int i = 0; i < MAX_FILTER_LOOPS; i++) { int changes = 0; - for (PropertyFilter filter : context.current().getPropertyFilters()) { + for (PropertyFilter filter : context.getConfigurationContext().getPropertyFilters()) { String value = filteredValue!=null?filteredValue.getValue():null; filteredValue = filter.filterProperty(filteredValue, context); String newValue = filteredValue!=null?filteredValue.getValue():null; diff --git a/code/spi-support/src/test/java/org/apache/tamaya/spisupport/MockedConfigurationContext.java b/code/spi-support/src/test/java/org/apache/tamaya/spisupport/MockedConfigurationContext.java index 02f1ede..0f941bf 100644 --- a/code/spi-support/src/test/java/org/apache/tamaya/spisupport/MockedConfigurationContext.java +++ b/code/spi-support/src/test/java/org/apache/tamaya/spisupport/MockedConfigurationContext.java @@ -40,7 +40,7 @@ public class MockedConfigurationContext implements ConfigurationContext { } @Override - public Map<String, String> getMetaData() { + public Map<String, String> getMetaData(String key) { return metaData; }
