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-extensions.git
commit 72018a4e344b97edc67de0bc267f0fdae096342c Author: Anatole Tresch <[email protected]> AuthorDate: Thu Feb 21 09:51:31 2019 +0100 TAMAYA-378 Improved injection API for more transparent key resolution. --- .../apache/tamaya/cdi/ConfigurationProducer.java | 5 +- .../tamaya/cdi/TamayaCDIInjectionExtension.java | 27 +-- .../tamaya/cdi/TamayaSEInjectionExtension.java | 4 +- .../tamaya/cdi/ConfigurationProducerTest.java | 36 ++-- .../org/apache/tamaya/cdi/ConfiguredClass.java | 22 +-- .../org/apache/tamaya/cdi/NotFoundNoDefault.java | 10 +- .../java/org/apache/tamaya/inject/api/Config.java | 36 +++- ...nfigDefaultSections.java => ConfigSection.java} | 26 ++- .../apache/tamaya/inject/api/KeyResolution.java | 55 ++++++ .../org/apache/tamaya/inject/api/KeyResolver.java | 41 ++++ .../tamaya/inject/spi/AbsoluteKeyResolver.java | 60 ++++++ .../apache/tamaya/inject/spi/AutoKeyResolver.java | 92 +++++++++ .../apache/tamaya/inject/spi/FullKeyResolver.java | 67 +++++++ .../apache/tamaya/inject/spi/InjectionUtils.java | 209 +++++++++++++++------ .../tamaya/inject/spi/SimpleKeyResolver.java | 69 +++++++ .../tamaya/inject/spi/InjectionUtilsTest.java | 115 +++++++++--- .../internal/ConfigTemplateInvocationHandler.java | 6 +- .../tamaya/inject/internal/ConfiguredTypeImpl.java | 19 +- .../internal/DefaultConfigurationInjector.java | 5 +- .../tamaya/inject/internal/InjectionHelper.java | 23 +-- .../test/java/annottext/AnnotatedConfigBean.java | 12 +- .../java/annottext/AnnotatedConfigTemplate.java | 11 +- .../annottext/InheritedAnnotatedConfigBean.java | 5 +- .../java/annottext/NonAnnotatedConfigBean.java | 2 + .../apache/tamaya/inject/TamayaInjectionTest.java | 7 +- .../apache/tamaya/inject/TestPropertySource.java | 18 +- .../inject/internal/DefaultDynamicValueTest.java | 10 +- 27 files changed, 790 insertions(+), 202 deletions(-) diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java index 03a745d..cf71b45 100644 --- a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java @@ -58,9 +58,10 @@ public class ConfigurationProducer { return createDynamicValue(injectionPoint); } final Config annotation = injectionPoint.getAnnotated().getAnnotation(Config.class); - final ConfigDefaultSections typeAnnot = injectionPoint.getMember().getDeclaringClass().getAnnotation(ConfigDefaultSections.class); + final ConfigSection typeAnnot = injectionPoint.getMember().getDeclaringClass().getAnnotation(ConfigSection.class); final List<String> keys = TamayaCDIInjectionExtension.evaluateKeys(injectionPoint.getMember().getName(), - annotation != null ? annotation.value() : null, + annotation != null ? annotation.key() : null, + annotation != null ? annotation.alternateKeys() : null, typeAnnot != null ? typeAnnot.value() : null); final WithConfigOperator withOperatorAnnot = injectionPoint.getAnnotated().getAnnotation(WithConfigOperator.class); diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java index 23b05be..8ec1137 100644 --- a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java @@ -20,7 +20,7 @@ import org.apache.tamaya.ConfigException; import org.apache.tamaya.ConfigOperator; import org.apache.tamaya.Configuration; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.ConfigSection; import org.apache.tamaya.inject.api.WithConfigOperator; import org.apache.tamaya.inject.api.WithPropertyConverter; import org.apache.tamaya.spi.PropertyConverter; @@ -42,7 +42,7 @@ import java.util.logging.Logger; * CDI Extension module that adds injection mechanism for configuration. * * @see Config - * @see ConfigDefaultSections + * @see ConfigSection * @see ConfigException */ public class TamayaCDIInjectionExtension implements Extension { @@ -77,9 +77,10 @@ public class TamayaCDIInjectionExtension implements Extension { if (injectionPoint.getAnnotated().isAnnotationPresent(Config.class)) { LOG.fine("Configuring: " + injectionPoint); final Config annotation = injectionPoint.getAnnotated().getAnnotation(Config.class); - final ConfigDefaultSections typeAnnot = injectionPoint.getMember().getDeclaringClass().getAnnotation(ConfigDefaultSections.class); + final ConfigSection typeAnnot = injectionPoint.getMember().getDeclaringClass().getAnnotation(ConfigSection.class); final List<String> keys = evaluateKeys(injectionPoint.getMember().getName(), - annotation!=null?annotation.value():null, + annotation!=null?annotation.key():null, + annotation!=null?annotation.alternateKeys():null, typeAnnot!=null?typeAnnot.value():null); final WithConfigOperator withOperatorAnnot = injectionPoint.getAnnotated().getAnnotation(WithConfigOperator.class); if(withOperatorAnnot!=null){ @@ -159,20 +160,22 @@ public class TamayaCDIInjectionExtension implements Extension { * The effective keys are then combined with the sections given (if any) and only, if the given keys are not * absolute keys (surrounded by brackets). * @param memberName the default member name, not null. - * @param keys the keys, may be empty, or null. - * @param sections the default sections, may be empty. May also be null. + * @param mainKey the main key, may be empty, or null. + * @param alternateKeys the alternate keys, may be empty, or null. + * @param section the default section, may be empty. May also be null. * @return the createList of keys to be finally used for configuration resolution in order of * precedence. The first keys in the createList that could be successfully resolved define the final * configuration value. */ - public static List<String> evaluateKeys(String memberName, String[] keys, String[] sections) { + public static List<String> evaluateKeys(String memberName, String mainKey, String[] alternateKeys, String section) { List<String> effKeys = new ArrayList<>(); - if(keys!=null){ - effKeys.addAll(Arrays.asList(keys)); + if(mainKey!=null && !mainKey.trim().isEmpty()){ + effKeys.add(mainKey); } if (effKeys.isEmpty()) { effKeys.add(memberName); } + effKeys.addAll(Arrays.asList(alternateKeys)); ListIterator<String> iterator = effKeys.listIterator(); while (iterator.hasNext()) { String next = iterator.next(); @@ -180,13 +183,11 @@ public class TamayaCDIInjectionExtension implements Extension { // absolute key, strip away brackets, take key as is iterator.set(next.substring(1, next.length() - 1)); } else { - if (sections != null && sections.length>0) { + if (section != null && !section.trim().isEmpty()) { // Remove original entry, since it will be replaced with prefixed entries iterator.remove(); // Add prefixed entries, including absolute (root) entry for "" area keys. - for (String area : sections) { - iterator.add(area.isEmpty() ? next : area + '.' + next); - } + iterator.add(section + '.' + next); } } } diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaSEInjectionExtension.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaSEInjectionExtension.java index 024b895..4a51863 100644 --- a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaSEInjectionExtension.java +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaSEInjectionExtension.java @@ -21,7 +21,7 @@ package org.apache.tamaya.cdi; import org.apache.tamaya.inject.ConfigurationInjection; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.ConfigSection; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; @@ -87,7 +87,7 @@ public final class TamayaSEInjectionExtension implements Extension { } private boolean isConfigured(Class type) { - if (type.getAnnotation(ConfigDefaultSections.class) != null) { + if (type.getAnnotation(ConfigSection.class) != null) { return true; } // if no class level annotation is there we might have field level annotations only diff --git a/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfigurationProducerTest.java b/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfigurationProducerTest.java index b3459b7..d070797 100644 --- a/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfigurationProducerTest.java +++ b/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfigurationProducerTest.java @@ -116,51 +116,51 @@ public class ConfigurationProducerTest { private Provider<Integer> providerIntegerAsMethodParam; @Inject - @Config(value = "string.value", defaultValue = "defaultString") + @Config(key = "string.value", defaultValue = "defaultString") private String string; @Inject - @Config(value = "string.value", defaultValue = "defaultString") + @Config(key = "string.value", defaultValue = "defaultString") private Optional<String> optionalString; @Inject - @Config(value = "string.value", defaultValue = "defaultString") + @Config(key = "string.value", defaultValue = "defaultString") private Provider<String> providerString; @Inject - @Config(value = "defaultString.value", defaultValue = "defaultString") + @Config(key = "defaultString.value", defaultValue = "defaultString") private String defaultString; @Inject - @Config(value = "file.value", defaultValue = "./") + @Config(key = "file.value", defaultValue = "./") private File file; @Inject - @Config(value = "defaultFile.value", defaultValue = "./") + @Config(key = "defaultFile.value", defaultValue = "./") private File defaultFile; @Inject - @Config(value = "boolean.value", defaultValue = "true") + @Config(key = "boolean.value", defaultValue = "true") private Boolean aBoolean; @Inject - @Config(value = "defaultBoolean.value", defaultValue = "true") + @Config(key = "defaultBoolean.value", defaultValue = "true") private Boolean defaultBoolean; @Inject - @Config(value = "integer.value", defaultValue = "45") + @Config(key = "integer.value", defaultValue = "45") private Integer integer; @Inject - @Config(value = "defaultInteger.value", defaultValue = "45") + @Config(key = "defaultInteger.value", defaultValue = "45") private Integer defaultInteger; @Inject - @Config(value = "integer.value", defaultValue = "45") + @Config(key = "integer.value", defaultValue = "45") private Optional<Integer> optionalInteger; @Inject - @Config(value = "integer.value", defaultValue = "45") + @Config(key = "integer.value", defaultValue = "45") private Provider<Integer> providerInteger; public String getString() { @@ -197,32 +197,32 @@ public class ConfigurationProducerTest { @Inject - public void setStringAsMethodParam(@Config(value = "string.value", defaultValue = "defaultString") String stringAsMethodParam) { + public void setStringAsMethodParam(@Config(key = "string.value", defaultValue = "defaultString") String stringAsMethodParam) { this.stringAsMethodParam = stringAsMethodParam; } @Inject - public void setIntegerAsMethodParam(@Config(value = "integer.value", defaultValue = "45")Integer integerAsMethodParam) { + public void setIntegerAsMethodParam(@Config(key = "integer.value", defaultValue = "45")Integer integerAsMethodParam) { this.integerAsMethodParam = integerAsMethodParam; } @Inject - public void setOptionalStringAsMethodParam(@Config(value = "string.value", defaultValue = "defaultString") Optional<String> optionalStringAsMethodParam) { + public void setOptionalStringAsMethodParam(@Config(key = "string.value", defaultValue = "defaultString") Optional<String> optionalStringAsMethodParam) { this.optionalStringAsMethodParam = optionalStringAsMethodParam; } @Inject - public void setOptionalIntegerAsMethodParam(@Config(value = "integer.value", defaultValue = "45") Optional<Integer> optionalIntegerAsMethodParam) { + public void setOptionalIntegerAsMethodParam(@Config(key = "integer.value", defaultValue = "45") Optional<Integer> optionalIntegerAsMethodParam) { this.optionalIntegerAsMethodParam = optionalIntegerAsMethodParam; } @Inject - public void setProviderStringAsMethodParam(@Config(value = "string.value", defaultValue = "defaultString") Provider<String> providerStringAsMethodParam) { + public void setProviderStringAsMethodParam(@Config(key = "string.value", defaultValue = "defaultString") Provider<String> providerStringAsMethodParam) { this.providerStringAsMethodParam = providerStringAsMethodParam; } @Inject - public void setProviderIntegerAsMethodParam(@Config(value = "integer.value", defaultValue = "45") Provider<Integer> providerIntegerAsMethodParam) { + public void setProviderIntegerAsMethodParam(@Config(key = "integer.value", defaultValue = "45") Provider<Integer> providerIntegerAsMethodParam) { this.providerIntegerAsMethodParam = providerIntegerAsMethodParam; } diff --git a/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfiguredClass.java b/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfiguredClass.java index b159a47..6b992c5 100644 --- a/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfiguredClass.java +++ b/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/ConfiguredClass.java @@ -46,13 +46,13 @@ public class ConfiguredClass{ // because our provider runs in @ApplicationScope, and null @Producer has to be in // @Dependent scope (CDI 2.0 spec section 3.2). Optional<> below is probably your // better idea. - @Config(value="stringMissingValue", required=false) + @Config(key="stringMissingValue", required=false) private String stringMissingValue; - @Config(value = {"a.b.c.key1","a.b.c.key2","a.b.c.key3"}, defaultValue = "The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.") + @Config(key = "a.b.c.key1", alternateKeys = {"a.b.c.key2","a.b.c.key3"}, defaultValue = "The current \\${JAVA_HOME} env property is ${env:JAVA_HOME}.") String value1; - @Config({"foo","a.b.c.key2"}) + @Config(key = "foo", alternateKeys = {"a.b.c.key2"}) private String value2; @Config(defaultValue = "N/A") @@ -70,10 +70,10 @@ public class ConfiguredClass{ @Config private boolean booleanT; - @Config("BD") + @Config(key = "BD") private BigDecimal bigNumber; - @Config("double1") + @Config(key = "[double1]") private double doubleValue; @Config @@ -83,24 +83,24 @@ public class ConfiguredClass{ @Config private Optional<String> injectedOptionalStringWithValue; - @Config(value="optionalStringMissingValue", required=false) + @Config(key="optionalStringMissingValue", required=false) private Optional<String> optionalStringMissingValue; @Inject - @Config(value="injectedOptionalStringMissingValue", required=false) + @Config(key="injectedOptionalStringMissingValue", required=false) private Optional<String> injectedOptionalStringMissingValue; - @Config(value="optionalStringMissingValueWithDefault", defaultValue="optionalStringDefaultValue", required=false) + @Config(key="optionalStringMissingValueWithDefault", defaultValue="optionalStringDefaultValue", required=false) private Optional<String> optionalStringMissingValueWithDefault; @Inject - @Config(value="injectedOptionalStringMissingValueWithDefault", defaultValue="injectedOptionalStringDefaultValue", required=false) + @Config(key="injectedOptionalStringMissingValueWithDefault", defaultValue="injectedOptionalStringDefaultValue", required=false) private Optional<String> injectedOptionalStringMissingValueWithDefault; - @Config("double1") + @Config(key="[double1]") private Optional<Double> existingDouble; - @Config("foo-bar") + @Config(key="foo-bar") private Optional<Double> nonExistingDouble; public String getTestProperty() { diff --git a/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/NotFoundNoDefault.java b/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/NotFoundNoDefault.java index 6e4e50e..1f29bbf 100644 --- a/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/NotFoundNoDefault.java +++ b/modules/injection/cdi/src/test/java/org/apache/tamaya/cdi/NotFoundNoDefault.java @@ -28,23 +28,23 @@ import java.time.Duration; public class NotFoundNoDefault { @Inject - @Config("string.bla") + @Config(key = "string.bla") private String string; @Inject - @Config("file.bla") + @Config(key = "file.bla") private File file; @Inject - @Config("duration.bla") + @Config(key = "duration.bla") private Duration duration; @Inject - @Config("boolean.bla") + @Config(key = "boolean.bla") private Boolean aBoolean; @Inject - @Config("integer.bla") + @Config(key = "integer.bla") private Integer integer; public String getString() { diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/Config.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/Config.java index 7f636d3..d18f8bf 100644 --- a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/Config.java +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/Config.java @@ -93,14 +93,42 @@ public @interface Config { String UNCONFIGURED_VALUE = "org.apache.tamaya.config.configproperty.unconfigureddvalue"; /** - * Defines the configuration property keys to be used. Hereby the first non null createValue evaluated is injected as - * property createValue. + * Defines the main configuration property key to be used. The final target property is evaluated based on + * the {@link #keyResolver()} strategy, by default {@link KeyResolution#AUTO}. * - * @return the property keys, not null. If empty, the field or property name (of a setter method) being injected + * @return the main property key, not null. If empty, the field or property name (of a setter method) being injected * is used by default. */ @Nonbinding - String[] value() default {}; + String key() default ""; + + /** + * Allows to customize the key resolution strategy how the {@link #key()} and {@link #alternateKeys()}values + * should be used to evaluate the final main target configuration keys. Hereby the default resolution + * works as follows: + * <ol> + * <li>The containing class <b>does not</b> have a {@link ConfigSection} annotation and the field/method does not have + * a {@link Config} annotation: the main key equals to + * {@code Owning.class.getSimpleName() + '.' + propertyKey}.</li> + * <li>The containing class <b>does not</b> have a {@link ConfigSection} annotation: the main key equals to + * {@code propertyKey}.</li> + * <li>The containing class <b>does</b> have a {@link ConfigSection} annotation: the main key equals to + * {@code areaAnnotation.getValue() + '.' + propertyKey}.</li> + * </ol> + * + * @return the key resolution strategy, never null. + */ + @Nonbinding + Class<? extends KeyResolver> keyResolver() default KeyResolver.class; + + /** + * Defines the alternate configuration property keys to be used, if no value could be evaluated using the main + * {@link #key()}. All key values given are resolved using the {@link KeyResolution#ABSOLUTE} strategy. + * + * @return the property keys, not null. + */ + @Nonbinding + String[] alternateKeys() default {}; /** * The default createValue to be injected, if none of the configuration keys could be resolved. If no key has been diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/ConfigDefaultSections.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/ConfigSection.java similarity index 55% rename from modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/ConfigDefaultSections.java rename to modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/ConfigSection.java index a9025f6..c54be77 100644 --- a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/ConfigDefaultSections.java +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/ConfigSection.java @@ -34,12 +34,30 @@ import java.lang.annotation.Target; */ @Retention(RetentionPolicy.RUNTIME) @Target(value = { ElementType.TYPE }) -public @interface ConfigDefaultSections { +public @interface ConfigSection { /** - * Allows to declare an section names that are prepended to resolve relative configuration keys. - * @return the section names to be used for key resolution. + * Allows to declare an section name that is prepended to resolve relative configuration keys. + * @return the section name to be used for key resolution. */ - String[] value() default {}; + String value() default ""; + + /** + * Allows to customize the <i>default</i> key resolution strategy how the {@code key()} and {@code alternateKeys()} + * values should be used to evaluate the final main target configuration keys. Hereby the default resolution + * works as follows: + * <ol> + * <li>The containing class <b>does not</b> have a {@link ConfigSection} annotation and the field/method does not have + * a {@link Config} annotation: the main key equals to + * {@code Owning.class.getSimpleName() + '.' + propertyKey}.</li> + * <li>The containing class <b>does not</b> have a {@link ConfigSection} annotation: the main key equals to + * {@code propertyKey}.</li> + * <li>The containing class <b>does</b> have a {@link ConfigSection} annotation: the main key equals to + * {@code areaAnnotation.getValue() + '.' + propertyKey}.</li> + * </ol> + * + * @return the key resolution strategy, never null. + */ + Class<? extends KeyResolver> keyResolver() default KeyResolver.class; } diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/KeyResolution.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/KeyResolution.java new file mode 100644 index 0000000..2c68766 --- /dev/null +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/KeyResolution.java @@ -0,0 +1,55 @@ +///* +// * 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.inject.api; +// +///** +// * Resolution strategy for the config key. +// */ +//public enum KeyResolution { +// /** +// * The targeting key is evaluated as follows: +// * <ol> +// * <li>The containing class <b>does not</b> have a {@link ConfigArea} annotation and the field/method does not have +// * a {@link Config} annotation: the main key equals to +// * * {@code Owning.class.getSimpleName() + '.' + propertyKey}. This equals to {@link #RELATIVE_SIMPLE}.</li> +// * <li>The containing class <b>does not</b> have a {@link ConfigArea} annotation: the main key equals to +// * {@code propertyKey}. This equals to {@link #ABSOLUTE}.</li> +// * <li>The containing class <b>does</b> have a {@link ConfigArea} annotation: the main key equals to +// * {@code areaAnnotation.getValue() + '.' + propertyKey}.</li> +// * </ol> +// * This is the default key resolution strategy. +// */ +// AUTO, +// +// /** +// * The targeting key is evaluated to {@code Owner.class.getSimpleName() + '.' + propertyKey}. +// */ +// RELATIVE_SIMPLE, +// +// /** +// * The targeting key is evaluated to {@code Owner.class.getName() + '.' + propertyKey}. +// */ +// RELATIVE_FQN, +// +// /** +// * The targeting key is evaluated using the evaluaed {@code propertyKey} only. +// */ +// ABSOLUTE +// +//} diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/KeyResolver.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/KeyResolver.java new file mode 100644 index 0000000..e409292 --- /dev/null +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/api/KeyResolver.java @@ -0,0 +1,41 @@ +/* + * 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.inject.api; + +import java.lang.reflect.Member; +import java.util.List; + +/** + * Implementations of this class can be configured to delegate the resolution of configuration lookup keys. + * The default resolution in place is + */ +@FunctionalInterface +public interface KeyResolver { + + /** + * Evaluates the effective configuration keys in order of significance. + * @param propertyKeys the main property keys, never null. The first key is the most significant property key, whereas + * subsequent keys may be derived, e.g. by replacing '_' or camel case with '.' etc. + * @param fallbackKeys the configured fallback keys. + * @param member the annotated {@link java.lang.reflect.Field} or {@link java.lang.reflect.Method}. + * @return the absolute keys to be evaluated in order of precedence (most significant first). + */ + List<String> resolveKeys(List<String> propertyKeys, List<String> fallbackKeys, Member member); + +} \ No newline at end of file diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/AbsoluteKeyResolver.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/AbsoluteKeyResolver.java new file mode 100644 index 0000000..95a8aaf --- /dev/null +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/AbsoluteKeyResolver.java @@ -0,0 +1,60 @@ +/* + * 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.inject.spi; + +import org.apache.tamaya.inject.api.KeyResolver; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Key resolver, which evaluates the keys to {@code propertyKey} only. + */ +public final class AbsoluteKeyResolver implements KeyResolver { + + @Override + public List<String> resolveKeys(List<String> propertyKeys, List<String> alternateKeys, Member member) { + List<String> result = new ArrayList<>(); + String sectionName = getSectionName(member); + for(String key:propertyKeys) { + result.add(evaluateKey(sectionName, key)); + } + for(String fk:alternateKeys){ + result.add(evaluateKey(sectionName, fk)); + } + return result; + } + + private String evaluateKey(String sectionName, String key) { + key = key.trim(); + if(key.startsWith("[") && key.endsWith("]")) { + return key.substring(1, key.length()-1); + }else if(sectionName!=null) { + return sectionName + "." + key; + } + return key; + } + + private String getSectionName(Member member) { + return null; + } +} diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/AutoKeyResolver.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/AutoKeyResolver.java new file mode 100644 index 0000000..446c0f5 --- /dev/null +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/AutoKeyResolver.java @@ -0,0 +1,92 @@ +/* + * 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.inject.spi; + +import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.api.ConfigSection; +import org.apache.tamaya.inject.api.KeyResolver; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Default key resolver which uses the following resolution strategy: + * <ol> + * <li>The containing class <b>does not</b> have a {@link ConfigSection} annotation and the field/method does not have + * a {@link Config} annotation: the main key equals to + * {@code Owning.class.getSimpleName() + '.' + propertyKey}.</li> + * <li>The containing class <b>does not</b> have a {@link ConfigSection} annotation: the main key equals to + * {@code propertyKey}.</li> + * <li>The containing class <b>does</b> have a {@link ConfigSection} annotation: the main key equals to + * {@code areaAnnotation.getValue() + '.' + propertyKey}.</li> + * </ol> + */ +public final class AutoKeyResolver implements KeyResolver { + + @Override + public List<String> resolveKeys(List<String> propertyKeys, List<String> alternateKeys, Member member) { + List<String> result = new ArrayList<>(); + String sectionName = getSectionName(member); + for(String key:propertyKeys) { + if (sectionName != null) { + result.add(evaluateKey(sectionName, key)); + } else { + result.add(key); + } + } + for(String fk:alternateKeys){ + result.add(evaluateKey(sectionName, fk)); + } + return result; + } + + private String evaluateKey(String sectionName, String key) { + key = key.trim(); + if(key.startsWith("[") && key.endsWith("]")) { + return key.substring(1, key.length()-1); + }else if(sectionName!=null) { + return sectionName + "." + key; + } + return key; + } + + private String getSectionName(Member member) { + if(member instanceof Field){ + Field f = (Field)member; + return getSectionName(f.getDeclaringClass()); + }else if(member instanceof Method){ + Method m = (Method)member; + return getSectionName(m.getDeclaringClass()); + }else{ + return null; + } + + } + + private String getSectionName(Class owner) { + ConfigSection sectionAnnot = (ConfigSection)owner.getAnnotation(ConfigSection.class); + if(sectionAnnot!=null && !sectionAnnot.value().isEmpty()){ + return sectionAnnot.value(); + } + return owner.getSimpleName(); + } +} diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/FullKeyResolver.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/FullKeyResolver.java new file mode 100644 index 0000000..a17acb0 --- /dev/null +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/FullKeyResolver.java @@ -0,0 +1,67 @@ +/* + * 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.inject.spi; + +import org.apache.tamaya.inject.api.KeyResolver; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Key resolver, which evaluates the keys to {@code Owner.class.getName() + '.' + propertyKey}. + */ +public final class FullKeyResolver implements KeyResolver { + + @Override + public List<String> resolveKeys(List<String> propertyKeys, List<String> alternateKeys, Member member) { + List<String> result = new ArrayList<>(); + String sectionName = getSectionName(member); + for(String key:propertyKeys) { + if (sectionName != null) { + result.add(sectionName + "." + key); + } else { + result.add(key); + } + } + for(String fk:alternateKeys){ + if(sectionName!=null){ + result.add(sectionName+"."+fk); + }else{ + result.add(fk); + } + } + return result; + } + + private String getSectionName(Member member) { + if(member instanceof Field){ + Field f = (Field)member; + return f.getDeclaringClass().getName(); + }else if(member instanceof Method){ + Method m = (Method)member; + return m.getDeclaringClass().getName(); + }else{ + return null; + } + + } +} diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/InjectionUtils.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/InjectionUtils.java index 2702676..806f3ce 100644 --- a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/InjectionUtils.java +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/InjectionUtils.java @@ -19,112 +19,201 @@ package org.apache.tamaya.inject.spi; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.ConfigSection; +import org.apache.tamaya.inject.api.KeyResolver; +import org.apache.tamaya.inject.api.NoConfig; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Utility class with several commonly used functions. */ public final class InjectionUtils { - private InjectionUtils(){} + private static final Logger LOG = Logger.getLogger(InjectionUtils.class.getName()); + + private static final KeyResolver AUTO_RESOLVER = new AutoKeyResolver(); + private InjectionUtils(){} /** - * Collects all keys to be be accessed as defined by any annotations of type - * {@link ConfigDefaultSections}, {@link Config}. + * Collects all keys to be be accessed, hereby the first key + * is the main key, subsequent keys are the fallback keys. * @param field the (optionally) annotated field instance * @return the regarding key createList to be accessed fomr the {@link org.apache.tamaya.Configuration}. */ public static List<String> getKeys(Field field) { - ConfigDefaultSections areasAnnot = field.getDeclaringClass().getAnnotation(ConfigDefaultSections.class); - return InjectionUtils.evaluateKeys(field, areasAnnot, field.getAnnotation(Config.class)); + if(field.isAnnotationPresent(NoConfig.class)){ + return Collections.emptyList(); + } + return InjectionUtils.evaluateKeys(field, field.getAnnotation(Config.class)); } /** - * Collects all keys to be be accessed as defined by any annotations of type - * {@link ConfigDefaultSections}, {@link Config}. + * Collects all keys to be be accessed, hereby the first key + * is the main key, subsequent keys are the fallback keys. * @param method the (optionally) annotated method instance * @return the regarding key createList to be accessed fomr the {@link org.apache.tamaya.Configuration}. */ public static List<String> getKeys(Method method) { - ConfigDefaultSections areasAnnot = method.getDeclaringClass().getAnnotation(ConfigDefaultSections.class); - return InjectionUtils.evaluateKeys(method, areasAnnot, method.getAnnotation(Config.class)); + Config configAnnot = method.getAnnotation(Config.class); + if(method.isAnnotationPresent(NoConfig.class) || configAnnot==null){ + return Collections.emptyList(); + } + return InjectionUtils.evaluateKeys(method, configAnnot); + } + + /** + * Evaluates all absolute configuration keys based on the member name found, hereby the first key + * is the main key, subsequent keys are the fallback keys. + * + * @param member the member, not null. + * @return the createList of current keys in order how they should be processed/looked up. + */ + public static List<String> getMemberKeys(Member member) { + if(member instanceof Field){ + return getKeys((Field)member); + } else if(member instanceof Method){ + return getKeys((Method)member); + } + return Collections.emptyList(); } /** - * Evaluates all absolute configuration keys based on the member name found. + * Evaluates all absolute configuration keys based on the member name found, hereby the first key + * is the main key, subsequent keys are the fallback keys. * - * @param member member to analyze. - * @param sectionAnnot the (optional) annotation defining areas to be looked up. + * @param configAnnot the (optional) config annotation * @return the createList of current keys in order how they should be processed/looked up. */ - public static List<String> evaluateKeys(Member member, ConfigDefaultSections sectionAnnot) { - List<String> keys = new ArrayList<>(); - List<String> areaKeys = evaluateSectionKeys(member, sectionAnnot); - String key = null; - String name = member.getName(); - if (name.startsWith("get") || name.startsWith("setCurrent")) { - key = Character.toLowerCase(name.charAt(3)) + name.substring(4); - } else { - key = Character.toLowerCase(name.charAt(0)) + name.substring(1); - } - for(String areaKey:areaKeys) { - keys.add(areaKey + '.' + key); - } - keys.add(key); - return keys; + private static List<String> evaluateKeys(Method method, Config configAnnot) { + List<String> propertyKeys = getPropertyKeys(method, configAnnot); + KeyResolver keyResolver = AUTO_RESOLVER; + ConfigSection sectionAnnot = method.getDeclaringClass().getAnnotation(ConfigSection.class); + if(sectionAnnot!=null && !sectionAnnot.keyResolver().equals(KeyResolver.class)){ + try { + keyResolver = sectionAnnot.keyResolver().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Cannot create KeyResolver: " + sectionAnnot.keyResolver().getName(), e); + } + } + if(configAnnot!=null && !configAnnot.keyResolver().equals(KeyResolver.class)){ + try { + keyResolver = configAnnot.keyResolver().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Cannot create KeyResolver: " + sectionAnnot.keyResolver().getName(), e); + } + } + List<String> alternateKeys = new ArrayList<>(); + if(configAnnot!=null){ + alternateKeys.addAll(Arrays.asList(configAnnot.alternateKeys())); + } + return keyResolver.resolveKeys(propertyKeys, alternateKeys, method); } /** - * Evaluates all absolute configuration keys based on the annotations found in a class. - * - * @param member member to analyze. - * @param areasAnnot the (optional) annotation definining areas to be looked up. - * @param propertyAnnotation the annotation on field/method level that may defined one or - * several keys to be looked up (in absolute or relative form). - * @return the createList current keys in order how they should be processed/looked up. + * Evaluates all absolute configuration keys based on the member name found, hereby the first key + * is the main key, subsequent keys are the fallback keys. + * + * @param configAnnot the (optional) config annotation + * @return the createList of current keys in order how they should be processed/looked up. */ - public static List<String> evaluateKeys(Member member, ConfigDefaultSections areasAnnot, Config propertyAnnotation) { - if(propertyAnnotation==null){ - return evaluateKeys(member, areasAnnot); + private static List<String> evaluateKeys(Field field, Config configAnnot) { + List<String> propertyKeys = getPropertyKeys(field, configAnnot); + KeyResolver keyResolver = AUTO_RESOLVER; + ConfigSection sectionAnnot = field.getDeclaringClass().getAnnotation(ConfigSection.class); + if(sectionAnnot!=null && !sectionAnnot.keyResolver().equals(KeyResolver.class)){ + try { + keyResolver = sectionAnnot.keyResolver().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Cannot create KeyResolver: " + sectionAnnot.keyResolver().getName(), e); + } } + if(configAnnot!=null && !configAnnot.keyResolver().equals(KeyResolver.class)){ + try { + keyResolver = configAnnot.keyResolver().getDeclaredConstructor().newInstance(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Cannot create KeyResolver: " + sectionAnnot.keyResolver().getName(), e); + } + } + List<String> alternateKeys = new ArrayList<>(); + if(configAnnot!=null){ + alternateKeys.addAll(Arrays.asList(configAnnot.alternateKeys())); + } + return keyResolver.resolveKeys(propertyKeys, alternateKeys, field); + } + + private static List<String> getPropertyKeys(Method method, Config configAnnot) { + if(configAnnot!=null && !configAnnot.key().isEmpty()){ + return Arrays.asList(configAnnot.key()); + } + String name = method.getName(); + if (name.startsWith("get") || name.startsWith("set")) { + return expandKey(Character.toLowerCase(name.charAt(3)) + name.substring(4)); + } + return expandKey(Character.toLowerCase(name.charAt(0)) + name.substring(1)); + } + + private static List<String> expandKey(String key) { List<String> result = new ArrayList<>(); - List<String> memberKeys = new ArrayList<>(Arrays.asList(propertyAnnotation.value())); - if (memberKeys.isEmpty()) { - memberKeys.add(member.getName()); - } - List<String> areaKeys = evaluateSectionKeys(member, areasAnnot); - for(String memberKey:memberKeys){ - if (memberKey.startsWith("[") && memberKey.endsWith("]")) { - // absolute key, strip away brackets, take key as is - result.add(memberKey.substring(1, memberKey.length()-1)); - }else{ - for(String areaKey:areaKeys) { - result.add(areaKey + '.' + memberKey); + result.add(key); + if(result.contains("_")){ + result.add(key.replace("_", ".")); + } + String splittedCamelCase = trySplitCamelCase(key); + if(splittedCamelCase!=null){ + result.add(splittedCamelCase); + } + return result; + } + + private static String trySplitCamelCase(String key) { + String result = ""; + int start = 0; + int index = 0; + while(index < key.length()){ + char ch = key.charAt(index++); + if(Character.isAlphabetic(ch)) { + if (Character.isLowerCase(ch)) { + result += ch; + } else if (Character.isUpperCase(ch)) { + if(!result.isEmpty() && !result.endsWith(".")){ + result += "."; + } + result += Character.toLowerCase(ch); } - result.add(memberKey); + } + else{ + result += ch; } } + if(result.equals(key)){ + return null; + } return result; } - private static List<String> evaluateSectionKeys(Member member, ConfigDefaultSections sectionAnnot) { - List<String> areaKeys = new ArrayList<>(); - if (sectionAnnot != null && sectionAnnot.value().length>0) { - // Remove original entry, since it will be replaced with prefixed entries - areaKeys.addAll(Arrays.asList(sectionAnnot.value())); - }else{ - areaKeys.add(member.getDeclaringClass().getName()); - areaKeys.add(member.getDeclaringClass().getSimpleName()); + private static List<String> getPropertyKeys(Field field, Config configAnnot) { + if(configAnnot!=null && !configAnnot.key().isEmpty()){ + return Arrays.asList(configAnnot.key()); } - return areaKeys; + return expandKey(field.getName()); + } + + private static List<String> getPropertyNames(String name) { + List<String> result = new ArrayList<>(); + result.add(name); + // 1) Check for _, replace with dots + // 2) Check for camel case, add dots in lowercase + return result; } } diff --git a/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/SimpleKeyResolver.java b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/SimpleKeyResolver.java new file mode 100644 index 0000000..8a89d7c --- /dev/null +++ b/modules/injection/injection-api/src/main/java/org/apache/tamaya/inject/spi/SimpleKeyResolver.java @@ -0,0 +1,69 @@ +/* + * 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.inject.spi; + +import org.apache.tamaya.inject.api.KeyResolver; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Key resolver, which evaluates the keys to {@code Owner.class.getSimpleName() + '.' + propertyKey}. + */ +public final class SimpleKeyResolver implements KeyResolver { + + @Override + public List<String> resolveKeys(List<String> propertyKeys, List<String> alternateKeys, Member member) { + List<String> result = new ArrayList<>(); + String sectionName = getSectionName(member); + for(String key:propertyKeys) { + result.add(evaluateKey(sectionName, key)); + } + for(String fk:alternateKeys){ + result.add(evaluateKey(sectionName, fk)); + } + return result; + } + + private String evaluateKey(String sectionName, String key) { + key = key.trim(); + if(key.startsWith("[") && key.endsWith("]")) { + return key.substring(1, key.length()-1); + }else if(sectionName!=null) { + return sectionName + "." + key; + } + return key; + } + + private String getSectionName(Member member) { + if(member instanceof Field){ + Field f = (Field)member; + return f.getDeclaringClass().getSimpleName(); + }else if(member instanceof Method){ + Method m = (Method)member; + return m.getDeclaringClass().getSimpleName(); + }else{ + return null; + } + + } +} diff --git a/modules/injection/injection-api/src/test/java/org/apache/tamaya/inject/spi/InjectionUtilsTest.java b/modules/injection/injection-api/src/test/java/org/apache/tamaya/inject/spi/InjectionUtilsTest.java index fee1a58..144eab4 100644 --- a/modules/injection/injection-api/src/test/java/org/apache/tamaya/inject/spi/InjectionUtilsTest.java +++ b/modules/injection/injection-api/src/test/java/org/apache/tamaya/inject/spi/InjectionUtilsTest.java @@ -19,7 +19,7 @@ package org.apache.tamaya.inject.spi; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.ConfigSection; import org.junit.Test; import java.lang.reflect.Field; @@ -33,7 +33,7 @@ public class InjectionUtilsTest { @Test public void getKeysMethod() { class Klazz { - @Config({"val", "val2", "[vvv]"}) + @Config(key = "val", alternateKeys = {"[val2]", "vvv"}) public void setValue(String field){} } @@ -41,17 +41,73 @@ public class InjectionUtilsTest { List<String> foundKeys = InjectionUtils.getKeys(method); - assertThat(foundKeys).isNotNull() - .contains("org.apache.tamaya.inject.spi.InjectionUtilsTest$1Klazz.val", - "org.apache.tamaya.inject.spi.InjectionUtilsTest$1Klazz.val2", - "Klazz.val", - "Klazz.val2", - "val", + assertThat(foundKeys) + .isNotNull() + .hasSize(3) + .contains("Klazz.val", + "val2", + "Klazz.vvv"); + } + + @Test + public void getKeysMethod_Resolution_Absolute() { + class Klazz { + @Config(key = "val", keyResolver = AbsoluteKeyResolver.class, alternateKeys = {"val2", "vvv"}) + public void setValue(String field){} + } + + Method method = Klazz.class.getMethods()[0]; + + List<String> foundKeys = InjectionUtils.getKeys(method); + + assertThat(foundKeys) + .isNotNull() + .hasSize(3) + .contains("val", "val2", "vvv"); } @Test + public void getKeysMethod_Resolution_RELATIVE_FQN() { + class Klazz { + @Config(key = "val", keyResolver = FullKeyResolver.class, alternateKeys = {"val2", "vvv"}) + public void setValue(String field){} + } + + Method method = Klazz.class.getMethods()[0]; + + List<String> foundKeys = InjectionUtils.getKeys(method); + + assertThat(foundKeys) + .isNotNull() + .hasSize(3) + .contains("org.apache.tamaya.inject.spi.InjectionUtilsTest$3Klazz.val", + "org.apache.tamaya.inject.spi.InjectionUtilsTest$3Klazz.val2", + "org.apache.tamaya.inject.spi.InjectionUtilsTest$3Klazz.vvv"); + } + + @Test + public void getKeysMethod_Resolution_RELATIVE_SIMPLE() { + @ConfigSection("nottobetaken") + class Klazz { + @Config(key = "val", keyResolver = SimpleKeyResolver.class, alternateKeys = {"val2", "vvv"}) + public void setValue(String field){} + } + + Method method = Klazz.class.getMethods()[0]; + + List<String> foundKeys = InjectionUtils.getKeys(method); + + assertThat(foundKeys) + .isNotNull() + .hasSize(3) + .contains("Klazz.val", + "Klazz.val2", + "Klazz.vvv"); + } + + @Test public void getKeysReturnsEmptyListForNonAnnotatedField() { class Klazz { public String field; @@ -62,16 +118,13 @@ public class InjectionUtilsTest { Field field = Klazz.class.getFields()[0]; List<String> foundKeys = InjectionUtils.getKeys(field); - - assertThat(foundKeys).isNotNull() - .contains("org.apache.tamaya.inject.spi.InjectionUtilsTest$2Klazz.field", - "Klazz.field", - "field"); + assertThat(foundKeys).hasSize(1); + assertThat(foundKeys.get(0)).isEqualTo("Klazz.field"); } @Test - public void evaluateKeysWithSection() { - @ConfigDefaultSections("basic") + public void getKeysWithSection() { + @ConfigSection("basic") class Klazz { public String field; protected String protectedField; @@ -80,17 +133,16 @@ public class InjectionUtilsTest { Field field = Klazz.class.getFields()[0]; - List<String> foundKeys = InjectionUtils.evaluateKeys(field, Klazz.class.getAnnotation(ConfigDefaultSections.class)); - assertThat(foundKeys).isNotNull() - .contains("basic.field", - "field"); + List<String> foundKeys = InjectionUtils.getKeys(field); + assertThat(foundKeys).hasSize(1); + assertThat(foundKeys.get(0)).isEqualTo("basic.field"); } @Test - public void evaluateKeysWithSectionAndMemberAnnotation() { - @ConfigDefaultSections("basic") + public void getKeysWithSectionAndMemberAnnotation() { + @ConfigSection("basic") class Klazz { - @Config({"val", "[absoluteVal]"}) + @Config(key = "val", alternateKeys = {"relativeVal", "[absoluteVal]"}) public String field; protected String protectedField; private String privateField; @@ -98,17 +150,19 @@ public class InjectionUtilsTest { Field field = Klazz.class.getFields()[0]; - List<String> foundKeys = InjectionUtils.evaluateKeys(field, Klazz.class.getAnnotation(ConfigDefaultSections.class), - field.getAnnotation(Config.class)); + List<String> foundKeys = InjectionUtils.getKeys(field); + assertThat(foundKeys).hasSize(3); + assertThat(foundKeys.get(0)).isEqualTo("basic.val"); assertThat(foundKeys).isNotNull() - .contains("basic.val", "val", + .contains("basic.val", + "basic.relativeVal", "absoluteVal"); } @Test - public void evaluateKeysWithMemberAnnotation() { + public void getKeysWithMemberAnnotation() { class Klazz { - @Config({"val", "[absoluteVal]"}) + @Config(key="val", alternateKeys = "[absoluteVal]") public String field; protected String protectedField; private String privateField; @@ -116,10 +170,11 @@ public class InjectionUtilsTest { Field field = Klazz.class.getFields()[0]; - List<String> foundKeys = InjectionUtils.evaluateKeys(field, null, - field.getAnnotation(Config.class)); + List<String> foundKeys = InjectionUtils.getKeys(field); + assertThat(foundKeys).hasSize(2); + assertThat(foundKeys.get(0)).isEqualTo("Klazz.val"); assertThat(foundKeys).isNotNull() - .contains("Klazz.val", "val", + .contains("Klazz.val", "absoluteVal"); } } \ No newline at end of file diff --git a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java index 15b6c68..96e3297 100644 --- a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java +++ b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java @@ -58,12 +58,10 @@ public final class ConfigTemplateInvocationHandler implements InvocationHandler public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("toString".equals(method.getName())) { return "Configured Proxy -> " + this.type.getType().getName(); - } else if ("hashCode".equals(method.getName())) { - return Objects.hashCode(proxy); - } else if ("equals".equals(method.getName())) { - return Objects.equals(proxy, args[0]); } else if ("current".equals(method.getName())) { return config; + } else if (method.getDeclaringClass().equals(Object.class)) { + return method.invoke(proxy, args); } if (method.getReturnType() == DynamicValue.class) { return DefaultDynamicValue.of(proxy, method, config); diff --git a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfiguredTypeImpl.java b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfiguredTypeImpl.java index 1697888..0f70db7 100644 --- a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfiguredTypeImpl.java +++ b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/ConfiguredTypeImpl.java @@ -26,11 +26,10 @@ import java.util.logging.Logger; import org.apache.tamaya.ConfigException; import org.apache.tamaya.Configuration; -import org.apache.tamaya.ConfigurationProvider; import org.apache.tamaya.inject.api.ConfigAutoInject; import org.apache.tamaya.inject.api.NoConfig; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.ConfigSection; import org.apache.tamaya.inject.spi.ConfiguredField; import org.apache.tamaya.inject.spi.ConfiguredMethod; import org.apache.tamaya.inject.spi.ConfiguredType; @@ -64,8 +63,12 @@ public class ConfiguredTypeImpl implements ConfiguredType{ */ public ConfiguredTypeImpl(Class type) { this.type = Objects.requireNonNull(type); + if(type.isAnnotationPresent(NoConfig.class)){ + LOG.info(() -> "Type is excluded from config: " + type.getName()); + return; + } if(!isConfigured(type)){ - LOG.info("Auto-Configuring type: " + type.getName()); + LOG.info(() -> "Auto-Configuring type: " + type.getName()); initFields(type, true); initMethods(type, true); }else { @@ -86,17 +89,17 @@ public class ConfiguredTypeImpl implements ConfiguredType{ } for (Field f : type.getDeclaredFields()) { if (f.isAnnotationPresent(NoConfig.class)) { - LOG.finest("Ignored @NoConfig annotated field " + f.getClass().getName() + "#" + + LOG.finest(() -> "Ignored @NoConfig annotated field " + f.getClass().getName() + "#" + f.toGenericString()); continue; } if (Modifier.isFinal(f.getModifiers())) { - LOG.finest("Ignored final field " + f.getClass().getName() + "#" + + LOG.finest(() -> "Ignored final field " + f.getClass().getName() + "#" + f.toGenericString()); continue; } if (f.isSynthetic()) { - LOG.finest("Ignored synthetic field " + f.getClass().getName() + "#" + + LOG.finest(() -> "Ignored synthetic field " + f.getClass().getName() + "#" + f.toGenericString()); continue; } @@ -104,7 +107,7 @@ public class ConfiguredTypeImpl implements ConfiguredType{ if(isConfiguredField(f) || autoConfigure) { ConfiguredField configuredField = new ConfiguredFieldImpl(f); configuredFields.add(configuredField); - LOG.finer("Registered field " + f.getClass().getName() + "#" + + LOG.finer(() -> "Registered field " + f.getClass().getName() + "#" + f.toGenericString()); } } catch (Exception e) { @@ -182,7 +185,7 @@ public class ConfiguredTypeImpl implements ConfiguredType{ public static boolean isConfigured(Class type) { - if (type.getAnnotation(ConfigDefaultSections.class) != null) { + if (type.getAnnotation(ConfigSection.class) != null) { return true; } // if no class level annotation is there we might have field level annotations only diff --git a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java index 4202052..4145ab7 100644 --- a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java +++ b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java @@ -19,7 +19,6 @@ package org.apache.tamaya.inject.internal; import org.apache.tamaya.Configuration; -import org.apache.tamaya.ConfigurationProvider; import org.apache.tamaya.inject.ConfigurationInjector; import javax.annotation.Priority; @@ -34,7 +33,7 @@ import java.util.logging.Logger; import org.apache.tamaya.inject.api.NoConfig; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.ConfigSection; import org.apache.tamaya.inject.spi.ConfiguredType; import org.apache.tamaya.spi.ClassloaderAware; import org.osgi.service.component.annotations.Component; @@ -96,7 +95,7 @@ public class DefaultConfigurationInjector implements ConfigurationInjector, Clas * @return true, if the type, a method or field has Tamaya config annotation on it. */ private boolean isConfigAnnotated(Class<?> type) { - if(type.getClass().isAnnotationPresent(ConfigDefaultSections.class)){ + if(type.getClass().isAnnotationPresent(ConfigSection.class)){ return true; } for (Field f : type.getDeclaredFields()) { diff --git a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/InjectionHelper.java b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/InjectionHelper.java index c3df063..075a5c8 100644 --- a/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/InjectionHelper.java +++ b/modules/injection/standalone/src/main/java/org/apache/tamaya/inject/internal/InjectionHelper.java @@ -28,12 +28,10 @@ import java.util.logging.Logger; import org.apache.tamaya.ConfigException; import org.apache.tamaya.Configuration; -import org.apache.tamaya.ConfigurationProvider; import org.apache.tamaya.TypeLiteral; import org.apache.tamaya.events.ConfigEventManager; import org.apache.tamaya.events.spi.BaseConfigEvent; import org.apache.tamaya.inject.api.Config; -import org.apache.tamaya.inject.api.ConfigDefaultSections; import org.apache.tamaya.inject.spi.InjectionUtils; import org.apache.tamaya.inject.api.WithPropertyConverter; import org.apache.tamaya.inject.spi.ConfiguredType; @@ -97,8 +95,7 @@ final class InjectionHelper { * @return the keys to be returned, or null. */ public static String getConfigValue(Method method, String[] retKey, Configuration config) { - ConfigDefaultSections areasAnnot = method.getDeclaringClass().getAnnotation(ConfigDefaultSections.class); - return getConfigValueInternal(method, areasAnnot, retKey, config); + return getConfigValueInternal(method, retKey, config); } /** @@ -117,8 +114,7 @@ final class InjectionHelper { * @return the keys to be returned, or null. */ public static String getConfigValue(Field field, String[] retKey, Configuration config) { - ConfigDefaultSections areasAnnot = field.getDeclaringClass().getAnnotation(ConfigDefaultSections.class); - return getConfigValueInternal(field, areasAnnot, retKey, config); + return getConfigValueInternal(field, retKey, config); } /** @@ -126,19 +122,14 @@ final class InjectionHelper { * * @return the keys to be returned, or null. */ - private static String getConfigValueInternal(AnnotatedElement element, ConfigDefaultSections areasAnnot, + private static String getConfigValueInternal(AnnotatedElement element, String[] retKey, Configuration config) { - Config prop = element.getAnnotation(Config.class); - List<String> keys; - if (prop == null) { - keys = InjectionUtils.evaluateKeys((Member) element, areasAnnot); - } else { - keys = InjectionUtils.evaluateKeys((Member) element, areasAnnot, prop); - } + List<String> keys = InjectionUtils.getMemberKeys((Member) element); String configValue = evaluteConfigValue(keys, retKey, config); if (configValue == null) { - if(prop!=null && !prop.defaultValue().equals(Config.UNCONFIGURED_VALUE)){ - return prop.defaultValue(); + Config configAnnot = element.getAnnotation(Config.class); + if(configAnnot!=null && !configAnnot.defaultValue().equals(Config.UNCONFIGURED_VALUE)){ + return configAnnot.defaultValue(); } } return configValue; diff --git a/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigBean.java b/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigBean.java index 3420977..8fbc3f5 100644 --- a/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigBean.java +++ b/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigBean.java @@ -21,6 +21,7 @@ package annottext; import org.apache.tamaya.inject.api.DynamicValue; import org.apache.tamaya.inject.api.NoConfig; import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.spi.AbsoluteKeyResolver; import java.util.ArrayList; import java.util.List; @@ -32,20 +33,20 @@ import java.util.List; */ public class AnnotatedConfigBean { - @Config(value = {"foo.bar.myprop", "mp", "common.testdata.myProperty"}, defaultValue = "ET") + @Config(key = "foo.bar.myprop", alternateKeys = {"mp", "common.testdata.myProperty"}, defaultValue = "ET") // @ConfigLoadPolicy(listener = MyListener.class) public String myParameter; - @Config("simple_value") + @Config(key = "simple_value") public String simpleValue; @Config String anotherValue; - @Config("host.name") + @Config(key = "host.name") private String hostName; - @Config("host.name") + @Config(key = "host.name") private DynamicValue<String> dynamicHostname; @NoConfig @@ -55,6 +56,7 @@ public class AnnotatedConfigBean { return anotherValue; } + @Config(keyResolver = AbsoluteKeyResolver.class) public String getHostName(){ return hostName; } @@ -70,7 +72,7 @@ public class AnnotatedConfigBean { public static final String CONSTANT = "a constant"; - @Config("java.version") + @Config(key = "[java.version]") void setJavaVersion(String version){ this.javaVersion = version; } diff --git a/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigTemplate.java b/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigTemplate.java index 8c6d692..87435b3 100644 --- a/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigTemplate.java +++ b/modules/injection/standalone/src/test/java/annottext/AnnotatedConfigTemplate.java @@ -18,30 +18,33 @@ */ package annottext; +import org.apache.tamaya.inject.api.ConfigSection; import org.apache.tamaya.inject.api.DynamicValue; import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.spi.AbsoluteKeyResolver; /** * An example showing some basic annotations, using an interface to be proxied by the * configuration system. * Created by Anatole on 15.02.14. */ +@ConfigSection(keyResolver = AbsoluteKeyResolver.class) public interface AnnotatedConfigTemplate { - @Config(value = {"foo.bar.myprop", "mp","common.testdata.myProperty"}, defaultValue = "ET") + @Config(key = "foo.bar.myprop", alternateKeys = {"mp","common.testdata.myProperty"}, defaultValue = "ET") // @ConfigLoadPolicy(listener = MyListener.class) String myParameter(); - @Config("simple_value") + @Config(key = "[simple_value]", keyResolver = AbsoluteKeyResolver.class) String simpleValue(); @Config String simplestValue(); - @Config("host.name") + @Config(key = "host.name", keyResolver = AbsoluteKeyResolver.class) String hostName(); - @Config("host.name") + @Config(key = "host.name", keyResolver = AbsoluteKeyResolver.class) DynamicValue<String> getDynamicValue(); } diff --git a/modules/injection/standalone/src/test/java/annottext/InheritedAnnotatedConfigBean.java b/modules/injection/standalone/src/test/java/annottext/InheritedAnnotatedConfigBean.java index 9952b18..f782ced 100644 --- a/modules/injection/standalone/src/test/java/annottext/InheritedAnnotatedConfigBean.java +++ b/modules/injection/standalone/src/test/java/annottext/InheritedAnnotatedConfigBean.java @@ -19,10 +19,13 @@ package annottext; import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.api.ConfigSection; +import org.apache.tamaya.inject.spi.AbsoluteKeyResolver; +@ConfigSection(keyResolver = AbsoluteKeyResolver.class) public class InheritedAnnotatedConfigBean extends AnnotatedConfigBean { - @Config("someMoreValue") + @Config public String someMoreValue; public String notConfigured; diff --git a/modules/injection/standalone/src/test/java/annottext/NonAnnotatedConfigBean.java b/modules/injection/standalone/src/test/java/annottext/NonAnnotatedConfigBean.java index a39e6df..aabb4ba 100644 --- a/modules/injection/standalone/src/test/java/annottext/NonAnnotatedConfigBean.java +++ b/modules/injection/standalone/src/test/java/annottext/NonAnnotatedConfigBean.java @@ -19,8 +19,10 @@ package annottext; import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.api.ConfigSection; import org.apache.tamaya.inject.api.DynamicValue; import org.apache.tamaya.inject.api.NoConfig; +import org.apache.tamaya.inject.spi.AbsoluteKeyResolver; import java.util.ArrayList; import java.util.List; diff --git a/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java b/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java index 5b6d55d..664b8ad 100644 --- a/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java +++ b/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java @@ -25,6 +25,7 @@ import annottext.NonAnnotatedConfigBean; import org.apache.tamaya.Configuration; import org.apache.tamaya.ConfigurationProvider; import org.apache.tamaya.spisupport.propertysource.MapPropertySource; +import org.junit.Before; import org.junit.Test; import java.util.HashMap; @@ -49,7 +50,7 @@ public class TamayaInjectionTest { assertEquals(testInstance.fullKey, null); assertEquals(testInstance.test2, "This is not setCurrent."); ConfigurationInjection.getConfigurationInjector().configure(testInstance); - assertEquals(testInstance.simple_value, "aSimpleValue"); + assertEquals(testInstance.simple_value, "aSimpleValue3"); assertEquals(testInstance.classFieldKey, "Class-Field-Value"); assertEquals(testInstance.fieldKey, "Field-Value"); assertEquals(testInstance.fullKey, "Fullkey-Value"); @@ -104,12 +105,12 @@ public class TamayaInjectionTest { assertNotNull(ConfigurationInjection.getConfigurationInjector()); AnnotatedConfigTemplate testInstance = ConfigurationInjection.getConfigurationInjector() .createTemplate(AnnotatedConfigTemplate.class); - assertEquals(testInstance.hostName(), "tamaya01.incubator.apache.org"); + assertEquals(testInstance.hostName(), "tamaya02.incubator.apache.org"); assertEquals(testInstance.myParameter(), "ET"); assertEquals(testInstance.simpleValue(), "aSimpleValue"); assertNotNull(testInstance.getDynamicValue()); assertTrue(testInstance.getDynamicValue().isPresent()); - assertEquals(testInstance.getDynamicValue().get(), "tamaya01.incubator.apache.org"); + assertEquals(testInstance.getDynamicValue().get(), "tamaya02.incubator.apache.org"); assertEquals(testInstance.hostName(), testInstance.getDynamicValue().get()); } diff --git a/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TestPropertySource.java b/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TestPropertySource.java index 20de8e8..0a1f988 100644 --- a/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TestPropertySource.java +++ b/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/TestPropertySource.java @@ -33,13 +33,23 @@ public class TestPropertySource implements PropertySource { public TestPropertySource(){ properties.put("env.stage", "ET"); - properties.put("simple_value", "aSimpleValue"); - properties.put("host.name", "tamaya01.incubator.apache.org"); + properties.put("AnnotatedConfigBean.host.name", "tamaya01.incubator.apache.org"); + properties.put("AnnotatedConfigBean.someMoreValue", "s'more"); + properties.put("AnnotatedConfigBean.anotherValue", "HALLO!"); + properties.put("AnnotatedConfigBean.simple_value", "aSimpleValue"); + + properties.put("hostName", "tamaya01.incubator.apache.org"); + properties.put("host.name", "tamaya02.incubator.apache.org"); properties.put("anotherValue", "HALLO!"); + properties.put("simple_value", "aSimpleValue"); + properties.put("someMoreValue", "s'more"); + + properties.put("AnnotatedConfigTemplate.simple_value", "aSimpleValue2"); + + properties.put("NonAnnotatedConfigBean.simple_value", "aSimpleValue3"); properties.put("NonAnnotatedConfigBean.classFieldKey", "Class-Field-Value"); properties.put("NonAnnotatedConfigBean.fieldKey", "Field-Value"); - properties.put("annottext.NonAnnotatedConfigBean.fullKey", "Fullkey-Value"); - properties.put("someMoreValue", "s'more"); + properties.put("NonAnnotatedConfigBean.fullKey", "Fullkey-Value"); } @Override diff --git a/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/internal/DefaultDynamicValueTest.java b/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/internal/DefaultDynamicValueTest.java index 2793052..713d141 100644 --- a/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/internal/DefaultDynamicValueTest.java +++ b/modules/injection/standalone/src/test/java/org/apache/tamaya/inject/internal/DefaultDynamicValueTest.java @@ -41,13 +41,13 @@ import static org.junit.Assert.*; */ public class DefaultDynamicValueTest { - @Config("a") + @Config(key="[a]") String myValue; - @Config("a") + @Config(key="[a]") String myValue2; - @Config("a") + @Config(key="[a]") void setterMethod(String value){ } @@ -211,13 +211,13 @@ public class DefaultDynamicValueTest { @Test public void testGetNewValue() throws Exception { - properties.put("a",PropertyValue.of("a","aValue","test")); + properties.put("a",PropertyValue.createValue("a","aValue")); DynamicValue val = DefaultDynamicValue.of(this, getClass().getDeclaredField("myValue"), config); val.setUpdatePolicy(UpdatePolicy.EXPLICIT); val.get(); assertNull(val.getNewValue()); - properties.put("a",PropertyValue.of("a","aValue2","test")); + properties.put("a",PropertyValue.createValue("a","aValue2")); val.get(); assertNotNull(val.getNewValue()); assertEquals("aValue2", val.getNewValue());
