Added further implementation support for DynamicValues and templates.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/4ddf46f7 Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/4ddf46f7 Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/4ddf46f7 Branch: refs/heads/master Commit: 4ddf46f748b3796db51ed09b43f29868eb0b7338 Parents: 1aa0dc4 Author: anatole <anat...@apache.org> Authored: Tue Feb 17 00:21:58 2015 +0100 Committer: anatole <anat...@apache.org> Committed: Tue Feb 17 00:21:58 2015 +0100 ---------------------------------------------------------------------- .../tamaya/inject/ConfigurationInjector.java | 7 + .../org/apache/tamaya/inject/DynamicValue.java | 374 ++-------------- .../ConfigTemplateInvocationHandler.java | 77 +++- .../tamaya/inject/internal/ConfiguredField.java | 88 +++- .../tamaya/inject/internal/ConfiguredType.java | 9 +- .../internal/DefaultConfigurationInjector.java | 18 + .../inject/internal/DefaultDynamicValue.java | 441 +++++++++++++++++++ .../tamaya/inject/internal/InjectionUtils.java | 35 +- .../java/annottext/AnnotatedConfigBean.java | 8 + .../java/annottext/AnnotatedConfigTemplate.java | 8 +- .../tamaya/inject/TamayaInjectionTest.java | 20 + 11 files changed, 722 insertions(+), 363 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/ConfigurationInjector.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/ConfigurationInjector.java b/modules/injection/src/main/java/org/apache/tamaya/inject/ConfigurationInjector.java index de1c29e..33c5227 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/ConfigurationInjector.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/ConfigurationInjector.java @@ -37,6 +37,13 @@ public interface ConfigurationInjector { */ <T> T configure(T instance); + /** + * Create a template implementting the annotated methods based on current configuration data. + * + * @param templateType the type of the template to be created. + */ + <T> T createTemplate(Class<T> templateType); + /** * Creates a supplier for configured instances of the given type {@code T}. http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/DynamicValue.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/DynamicValue.java b/modules/injection/src/main/java/org/apache/tamaya/inject/DynamicValue.java index 6075666..1979b55 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/DynamicValue.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/DynamicValue.java @@ -19,40 +19,27 @@ package org.apache.tamaya.inject; import java.beans.PropertyChangeEvent; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; import java.util.function.Supplier; -import java.util.logging.Logger; /** * A accessor for a single configured value. This can be used to support values that may change during runtime, * reconfigured or final. Hereby external code (could be Tamaya configuration listners or client code), can set a * new value. Depending on the {@link UpdatePolicy} the new value is immedeately active or it requires an active commit - * by client code. Similarly an instance also can ignore all later changes to the value. - * <h3>Implementation Details</h3> - * This class is + * by client code. Similarly an instance also can ignore all later changes to the value.<br/> + * Types of this interface can be used as injection targets in injected beans or as template resiult on configuration + * templates. + * <h3>Implementation Specification</h3> + * Implementation of this interface must be * <ul> * <li>Serializable, when also the item stored is serializable</li> * <li>Thread safe</li> * </ul> * @param <T> The type of the value. */ -public final class DynamicValue<T> implements Serializable { +public interface DynamicValue<T> { - private static final long serialVersionUID = -2071172847144537443L; - /** * Policy to control how new values are applied to this instance. */ @@ -73,69 +60,6 @@ public final class DynamicValue<T> implements Serializable { LOG_AND_DISCARD } - - /** The property name of the entry. */ - private String propertyName; - /** - * Policy that defines how new values are applied, be default it is applied initially once, but never updated - * anymore. - */ - private UpdatePolicy updatePolicy = UpdatePolicy.NEVER; - /** The current value, never null. */ - private transient Optional<T> value; - /** The new value, or null. */ - private transient Optional<T> newValue; - /** List of listeners that listen for changes. */ - private transient WeakList<Consumer<PropertyChangeEvent>> listeners; - - /** - * Returns an empty {@code DynamicValue} instance. No value is present for this - * DynamicValue. - * - * NOTE: Though it may be tempting to do so, avoid testing if an object - * is empty by comparing with {@code ==} against instances returned by - * {@code Option.empty()}. There is no guarantee that it is a singleton. - * Instead, use {@link #isPresent()}. - * - * @param <T> Type of the non-existent value - * @return an empty {@code DynamicValue} - */ - public static <T> DynamicValue<T> empty(String propertyName) { - return new DynamicValue<T>(propertyName, null); - } - - /** - * Constructor. - * @param propertyName the key of the property, not null. - * @param item the initial value. - */ - private DynamicValue(String propertyName, Optional<T> item){ - this.propertyName = Objects.requireNonNull(propertyName); - this.value = item; - } - - /** - * Creates a new instance. - * @param propertyName the key of the property, not null. - * @param value the initial value, not null. - * @param <T> the type - * @return a new instance, never null - */ - public static <T> DynamicValue<T> of(String propertyName, T value){ - return new DynamicValue<T>(propertyName, Optional.of(value)); - } - - /** - * Creates a new instance. - * @param propertyName the key of the property, not null. - * @param value the initial value - * @param <T> the target type. - * @return a new instance, never null - */ - public static <T> DynamicValue<T> ofNullable(String propertyName, T value){ - return value == null ? empty(propertyName) : of(propertyName, value); - } - /** * Performs a commit, if necessary, and returns the current value. * @@ -144,7 +68,7 @@ public final class DynamicValue<T> implements Serializable { * * @see DynamicValue#isPresent() */ - public T commitAndGet(){ + default T commitAndGet(){ commit(); return get(); } @@ -153,57 +77,30 @@ public final class DynamicValue<T> implements Serializable { * Commits a new value that has not been committed yet, make it the new value of the instance. On change any * registered listeners will be triggered. */ - public void commit(){ - synchronized (this){ - if(newValue!=null){ - PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, value.orElse(null), - newValue.orElse(null)); - value = newValue; - newValue = null; - for(Consumer<PropertyChangeEvent> consumer: listeners.get()){ - consumer.accept(evt); - } - } - } - } + void commit(); /** * Discards a new value that was published. No listeners will be informed. */ - public void discard(){ - newValue = null; - } - - + void discard(); /** * Access the {@link UpdatePolicy} used for updating this value. * @return the update policy, never null. */ - public UpdatePolicy getUpdatePolicy() { - return updatePolicy; - } + UpdatePolicy getUpdatePolicy(); /** * Add a listener to be called as weak reference, when this value has been changed. * @param l the listener, not null */ - public void addListener(Consumer<PropertyChangeEvent> l) { - if(listeners==null){ - listeners = new WeakList<>(); - } - listeners.add(l); - } + void addListener(Consumer<PropertyChangeEvent> l); /** * Removes a listener to be called, when this value has been changed. * @param l the listner to be removed, not null */ - public void removeListener(Consumer<PropertyChangeEvent> l) { - if(listeners!=null){ - listeners.remove(l); - } - } + void removeListener(Consumer<PropertyChangeEvent> l); /** * If a value is present in this {@code DynamicValue}, returns the value, @@ -214,64 +111,40 @@ public final class DynamicValue<T> implements Serializable { * * @see DynamicValue#isPresent() */ - public T get() { - return value.get(); - } + T get(); /** - * Method to apply a new value. Depending on the {@link UpdatePolicy} + * Method to check for and apply a new value. Depending on the {@link UpdatePolicy} * the value is immediately or deferred visible (or it may even be ignored completely). - * @param newValue the new value, may also be null. + * @return true, if a new value has been detected. The value may not be visible depending on the current + * {@link org.apache.tamaya.inject.DynamicValue.UpdatePolicy} in place. */ - public void setNewValue(T newValue){ - switch(this.updatePolicy){ - case IMMEDIATE: - this.newValue = Optional.ofNullable(newValue); - commit(); - break; - case LOG_AND_DISCARD: - Logger.getLogger(getClass().getName()).info("Discard change on " + this + ", newValue="+newValue); - this.newValue = null; - break; - case NEVER: - this.newValue = null; - break; - case EXPLCIT: - default: - this.newValue = Optional.ofNullable(newValue); - break; - } + boolean updateValue(); - } + /** + * Evaluates the current value dynamically from the underlying configuration. + * @return the current actual value, or null. + */ + T evaluateValue(); /** * Sets a new {@link UpdatePolicy}. * @param updatePolicy the new policy, not null. */ - public void setUpdatePolicy(UpdatePolicy updatePolicy){ - this.updatePolicy = Objects.requireNonNull(updatePolicy); - } + void setUpdatePolicy(UpdatePolicy updatePolicy); /** * Access a new value that has not yet been committed. * @return the uncommitted new value, or null. */ - public T getNewValue(){ - Optional<T> nv = newValue; - if(nv!=null){ - return nv.orElse(null); - } - return null; - } + T getNewValue(); /** * Return {@code true} if there is a value present, otherwise {@code false}. * * @return {@code true} if there is a value present, otherwise {@code false} */ - public boolean isPresent() { - return value.isPresent(); - } + boolean isPresent(); /** * If a value is present, invoke the specified consumer with the value, @@ -281,89 +154,10 @@ public final class DynamicValue<T> implements Serializable { * @throws NullPointerException if value is present and {@code consumer} is * null */ - public void ifPresent(Consumer<? super T> consumer) { - value.ifPresent(consumer); - } - - /** - * If a value is present, and the value matches the given predicate, - * return an {@code Optional} describing the value, otherwise return an - * empty {@code Optional}. - * - * @param predicate a predicate to apply to the value, if present - * @return an {@code Optional} describing the value of this {@code Optional} - * if a value is present and the value matches the given predicate, - * otherwise an empty {@code Optional} - * @throws NullPointerException if the predicate is null - */ - public DynamicValue<T> filter(Predicate<? super T> predicate) { - Objects.requireNonNull(predicate); - if (!isPresent()) { - return this; + default void ifPresent(Consumer<? super T> consumer){ + if(isPresent()){ + consumer.accept(get()); } - return predicate.test(value.get()) ? this : empty(propertyName); - } - - /** - * If a value is present, apply the provided mapping function to it, - * and if the result is non-null, return an {@code Optional} describing the - * result. Otherwise return an empty {@code Optional}. - * - * NOTE This method supports post-processing on optional values, without - * the need to explicitly check for a return status. For example, the - * following code traverses a stream of file names, selects one that has - * not yet been processed, and then opens that file, returning an - * {@code Optional<FileInputStream>}: - * - * <pre>{@code - * Optional<FileInputStream> fis = - * names.stream().filter(name -> !isProcessedYet(name)) - * .findFirst() - * .map(name -> new FileInputStream(name)); - * }</pre> - * - * Here, {@code findFirst} returns an {@code Optional<String>}, and then - * {@code map} returns an {@code Optional<FileInputStream>} for the desired - * file if one exists. - * - * @param <U> The type of the result of the mapping function - * @param mapper a mapping function to apply to the value, if present - * @return an {@code Optional} describing the result of applying a mapping - * function to the value of this {@code Optional}, if a value is present, - * otherwise an empty {@code Optional} - * @throws NullPointerException if the mapping function is null - */ - public <U> DynamicValue<U> map(Function<? super T, ? extends U> mapper) { - Objects.requireNonNull(mapper); - if (!isPresent()) { - return empty(propertyName); - } - return DynamicValue.ofNullable(propertyName, mapper.apply(value.get())); - } - - /** - * If a value is present, apply the provided {@code Optional}-bearing - * mapping function to it, return that result, otherwise return an empty - * {@code Optional}. This method is similar to {@link #map(Function)}, - * but the provided mapper is one whose result is already an {@code Optional}, - * and if invoked, {@code flatMap} does not wrap it with an additional - * {@code Optional}. - * - * @param <U> The type parameter to the {@code Optional} returned by - * @param mapper a mapping function to apply to the value, if present - * the mapping function - * @return the result of applying an {@code Optional}-bearing mapping - * function to the value of this {@code Optional}, if a value is present, - * otherwise an empty {@code Optional} - * @throws NullPointerException if the mapping function is null or returns - * a null result - */ - public <U> DynamicValue<U> flatMap(Function<? super T, DynamicValue<U>> mapper) { - Objects.requireNonNull(mapper); - if (!isPresent()) { - return empty(propertyName); - } - return Objects.requireNonNull(mapper.apply(value.get())); } /** @@ -373,9 +167,12 @@ public final class DynamicValue<T> implements Serializable { * be null * @return the value, if present, otherwise {@code other} */ - public T orElse(T other) { - return value.orElse(other); - } + default T orElse(T other){ + if(isPresent()){ + return get(); + } + return other; + } /** * Return the value if present, otherwise invoke {@code other} and return @@ -387,8 +184,11 @@ public final class DynamicValue<T> implements Serializable { * @throws NullPointerException if value is not present and {@code other} is * null */ - public T orElseGet(Supplier<? extends T> other) { - return value.orElseGet(other); + default T orElseGet(Supplier<? extends T> other){ + if(isPresent()){ + return get(); + } + return other.get(); } /** @@ -407,100 +207,22 @@ public final class DynamicValue<T> implements Serializable { * @throws NullPointerException if no value is present and * {@code exceptionSupplier} is null */ - public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { - return value.orElseThrow(exceptionSupplier); + default <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X{ + if(isPresent()){ + return get(); + } + throw exceptionSupplier.get(); } /** * Converts the instance to an {@link java.util.Optional} instance. * @return the corresponding Optional value. */ - public Optional<T> toOptional(){ - return value; - } - - /** - * Serialization implementation that strips away the non serializable Optional part. - * @param oos the output stream - * @throws IOException if serialization fails. - */ - private void writeObject(ObjectOutputStream oos)throws IOException { - oos.writeObject(updatePolicy); - if(isPresent()) { - oos.writeObject(this.value.get()); - } else{ - oos.writeObject(null); - } - } - - /** - * Reads an instance from the input stream. - * @param ois the object input stream - * @throws IOException if deserialization fails. - * @throws ClassNotFoundException - */ - private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { - this.updatePolicy = (UpdatePolicy)ois.readObject(); - if(isPresent()) { - this.value = Optional.of((T) ois.readObject()); - } - newValue = null; - } - - - /** - * Simple helper that allows keeping the listeners registered as weak references, hereby avoiding any - * memory leaks. - * @param <I> the type - */ - private class WeakList<I>{ - final List<WeakReference<I>> refs = new LinkedList<>(); - - /** - * Adds a new instance. - * @param t the new instance, not null. - */ - void add(I t){ - refs.add(new WeakReference<I>(t)); - } - - /** - * Removes a instance. - * @param t the instance to be removed. - */ - void remove(I t){ - synchronized (refs){ - for(Iterator<WeakReference<I>> iterator = refs.iterator();iterator.hasNext();){ - WeakReference<I> ref = iterator.next(); - I instance = ref.get(); - if(instance==null || instance == t){ - iterator.remove(); - break; - } - } - } - } - - - /** - * Access a list (copy) of the current instances that were not discarded by the GC. - * @return the list of accessible items. - */ - public List<I> get() { - synchronized (refs) { - List<I> res = new ArrayList<>(); - for (Iterator<WeakReference<I>> iterator = refs.iterator(); iterator.hasNext(); ) { - WeakReference<I> ref = iterator.next(); - I instance = ref.get(); - if(instance==null){ - iterator.remove(); - } else{ - res.add(instance); - } - } - return res; - } + default Optional<T> toOptional(){ + if(isPresent()){ + return Optional.of(get()); } + return Optional.empty(); } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java index 9e9ee73..4909207 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfigTemplateInvocationHandler.java @@ -18,11 +18,17 @@ */ package org.apache.tamaya.inject.internal; +import org.apache.tamaya.ConfigException; import org.apache.tamaya.Configuration; +import org.apache.tamaya.PropertyConverter; import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.inject.DynamicValue; +import org.apache.tamaya.inject.WithPropertyConverter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.Objects; /** @@ -30,43 +36,78 @@ import java.util.Objects; */ public final class ConfigTemplateInvocationHandler implements InvocationHandler { - /* - TODO - the given method (in case of a template) can use different caching strategies: - 1) no caching (always evaluate the values completely) - slow. - 2) instance caching (a cache per instance). - 3) classloader caching... - 4) global shared cache. - */ - - /** * The configured type. */ private ConfiguredType type; /** - * Creates a new handler instance. - * @param type the target type, not null. - * @param configurations overriding configurations to be used for evaluating the values for injection into {@code instance}, not null. - * If no such config is passed, the default configurationa provided by the current - * registered providers are used. + * The configuration instance of this proxy. */ - public ConfigTemplateInvocationHandler(Class<?> type, Configuration... configurations) { - Objects.requireNonNull(configurations); + private Configuration configuration; + /** + * Creates a new handler instance. + * + * @param type the target type, not null. + * @param configuration the configuration to be used for evaluating the values for injection into {@code instance}, + * not null. + */ + public ConfigTemplateInvocationHandler(Class<?> type, Configuration configuration) { + Objects.requireNonNull(configuration); this.type = new ConfiguredType(Objects.requireNonNull(type)); if (!type.isInterface()) { throw new IllegalArgumentException("Can only proxy interfaces as configuration templates."); } + this.configuration = Objects.requireNonNull(configuration); } @Override 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 ("get".equals(method.getName())) { + return this.configuration; + } + if (method.getReturnType() == DynamicValue.class) { + // Check for adapter/filter + Type targetType = method.getGenericReturnType(); + if (targetType == null) { + throw new ConfigException("Failed to evaluate target type for " + method.getDeclaringClass() + .getTypeName() + '.' + method.getName()); + } + if (targetType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) targetType; + Type[] types = pt.getActualTypeArguments(); + if (types.length != 1) { + throw new ConfigException("Failed to evaluate target type for " + method.getDeclaringClass() + .getTypeName() + '.' + method.getName()); + } + targetType = (Class) types[0]; + } + PropertyConverter<Object> propertyConverter = null; + WithPropertyConverter annot = method.getAnnotation(WithPropertyConverter.class); + if (annot != null) { + try { + propertyConverter = (PropertyConverter<Object>) annot.value().newInstance(); + } catch (Exception e) { + throw new ConfigException("Failed to instantiate annotated PropertyConverter on " + + method.getDeclaringClass().getTypeName() + + '.' + method.getName(), e); + } + } + return new DefaultDynamicValue<Object>(method.getName(), + this.configuration, TypeLiteral.of(targetType), propertyConverter, InjectionUtils.getKeys(method)); } String configValue = InjectionUtils.getConfigValue(method); - return InjectionUtils.adaptValue(method, TypeLiteral.of(method.getReturnType()), configValue); + Object result = InjectionUtils.adaptValue(method, TypeLiteral.of(method.getReturnType()), configValue); + if (result == null && method.isDefault()) { + result = method.getDefaultValue(); + } + return result; } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredField.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredField.java b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredField.java index 8bde306..a2a01ea 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredField.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredField.java @@ -18,16 +18,24 @@ */ package org.apache.tamaya.inject.internal; +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.ConfigurationProvider; +import org.apache.tamaya.PropertyConverter; +import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.inject.ConfigRoot; +import org.apache.tamaya.inject.ConfiguredProperty; +import org.apache.tamaya.inject.DynamicValue; +import org.apache.tamaya.inject.WithPropertyConverter; + import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.List; import java.util.Objects; - -import org.apache.tamaya.ConfigException; -import org.apache.tamaya.TypeLiteral; -import org.apache.tamaya.inject.ConfigRoot; -import org.apache.tamaya.inject.ConfiguredProperty; +import java.util.logging.Logger; /** * Small class that contains and manages all information anc access to a configured field and a concrete instance current @@ -35,8 +43,10 @@ import org.apache.tamaya.inject.ConfiguredProperty; * final keys by reflection. */ public class ConfiguredField { - - + /** + * The logger used. + */ + private static final Logger LOG = Logger.getLogger(ConfiguredField.class.getName()); /** * The configured field instance. */ @@ -59,12 +69,66 @@ public class ConfiguredField { * @throws ConfigException if evaluation or conversion failed. */ public void applyInitialValue(Object target) throws ConfigException { - String configValue = InjectionUtils.getConfigValue(this.annotatedField); - applyValue(target, configValue, false); + if (this.annotatedField.getType() == DynamicValue.class) { + initDynamicValue(target); + } else { + String configValue = InjectionUtils.getConfigValue(this.annotatedField); + applyValue(target, configValue, false); + } } /** + * This method instantiates and assigns a dynamic value. + * + * @param target the target instance, not null. + * @throws ConfigException if the configuration required could not be resolved or converted. + */ + public void initDynamicValue(Object target) throws ConfigException { + Objects.requireNonNull(target); + try { + // Check for adapter/filter + Type targetType = this.annotatedField.getGenericType(); + if (targetType == null) { + throw new ConfigException("Failed to evaluate target type for " + annotatedField.getAnnotatedType().getType().getTypeName() + + '.' + annotatedField.getName()); + } + if (targetType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) targetType; + Type[] types = pt.getActualTypeArguments(); + if (types.length != 1) { + throw new ConfigException("Failed to evaluate target type for " + annotatedField.getAnnotatedType().getType().getTypeName() + + '.' + annotatedField.getName()); + } + targetType = (Class) types[0]; + } + PropertyConverter<?> propertyConverter = null; + WithPropertyConverter annot = this.annotatedField.getAnnotation(WithPropertyConverter.class); + if (annot != null) { + try { + propertyConverter = annot.value().newInstance(); + } catch (Exception e) { + throw new ConfigException("Failed to instantiate annotated PropertyConverter on " + + annotatedField.getAnnotatedType().getType().getTypeName() + + '.' + annotatedField.getName(), e); + } + } + List<String> keys = InjectionUtils.getKeys(this.annotatedField); + Configuration configuration = ConfigurationProvider.getConfiguration(); + Object value = new DefaultDynamicValue(this.annotatedField.getName(), configuration, + TypeLiteral.of(targetType), propertyConverter, keys); + AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { + annotatedField.setAccessible(true); + return annotatedField; + }); + annotatedField.set(target, value); + } catch (Exception e) { + throw new ConfigException("Failed to annotation configured field: " + this.annotatedField.getDeclaringClass() + .getName() + '.' + annotatedField.getName(), e); + } + } + + /** * This method reapplies a changed configuration keys to the field. * * @param target the target instance, not null. @@ -111,4 +175,10 @@ public class ConfiguredField { return keys.contains(key); } + @Override + public String toString() { + return "ConfiguredField{" + + annotatedField.getName() + ": " + + " " + annotatedField.getAnnotatedType().getType().getTypeName() + '}'; + } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredType.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredType.java b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredType.java index 8c4f68f..8828598 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredType.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/ConfiguredType.java @@ -94,10 +94,6 @@ public class ConfiguredType { if (m.isDefault()) { addObserverMethod(m); } - } else { - if (m.isDefault()) { - addPropertySetter(m, prop); - } } } else { if (mAnnot != null) { @@ -192,4 +188,9 @@ public class ConfiguredType { public Class getType() { return this.type; } + + @Override + public String toString() { + return "ConfiguredType{"+ this.getType().getName() + '}'; + } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java index 6f6794b..baa397d 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultConfigurationInjector.java @@ -18,9 +18,11 @@ */ package org.apache.tamaya.inject.internal; +import org.apache.tamaya.ConfigurationProvider; import org.apache.tamaya.inject.ConfigurationInjector; import javax.annotation.Priority; +import java.lang.reflect.Proxy; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -54,6 +56,7 @@ public final class DefaultConfigurationInjector implements ConfigurationInjector * * @param instance the instance to be configured */ + @Override public <T> T configure(T instance) { Class type = Objects.requireNonNull(instance).getClass(); ConfiguredType configuredType = registerTypeInternal(type); @@ -61,6 +64,21 @@ public final class DefaultConfigurationInjector implements ConfigurationInjector return instance; } + /** + * Create a template implementting the annotated methods based on current configuration data. + * + * @param templateType the type of the template to be created. + */ + @Override + public <T> T createTemplate(Class<T> templateType) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if(cl==null){ + cl = this.getClass().getClassLoader(); + } + return (T)Proxy.newProxyInstance(cl,new Class[]{Supplier.class, Objects.requireNonNull(templateType)}, + new ConfigTemplateInvocationHandler(templateType, ConfigurationProvider.getConfiguration())); + } + @Override public <T> Supplier<T> getConfiguredSupplier(Supplier<T> supplier) { http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultDynamicValue.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultDynamicValue.java b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultDynamicValue.java new file mode 100644 index 0000000..9f97ad1 --- /dev/null +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/DefaultDynamicValue.java @@ -0,0 +1,441 @@ +/* + * 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.internal; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.PropertyConverter; +import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.inject.DynamicValue; + +import java.beans.PropertyChangeEvent; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Logger; + +/** + * A accessor for a single configured value. This can be used to support values that may change during runtime, + * reconfigured or final. Hereby external code (could be Tamaya configuration listners or client code), can set a + * new value. Depending on the {@link UpdatePolicy} the new value is immedeately active or it requires an active commit + * by client code. Similarly an instance also can ignore all later changes to the value. + * <h3>Implementation Details</h3> + * This class is + * <ul> + * <li>Serializable, when also the item stored is serializable</li> + * <li>Thread safe</li> + * </ul> + * + * @param <T> The type of the value. + */ +public final class DefaultDynamicValue<T> implements DynamicValue<T>, Serializable { + + private static final long serialVersionUID = -2071172847144537443L; + + /** + * The property name of the entry. + */ + private String propertyName; + /** + * The keys to be resolved. + */ + private String[] keys; + /** + * Back reference to the base configuration instance. This reference is used reevalaute the given property and + * compare the result with the previous value after a configuration change was triggered. + */ + private Configuration configuration; + /** + * The target type of the property used to lookup a matching {@link org.apache.tamaya.PropertyConverter}. + * If null, {@code propertyConverter} is set and used instead. + */ + private TypeLiteral<T> targetType; + /** + * The property converter to be applied, may be null. In the ladder case targetType is not null. + */ + private PropertyConverter<T> propertyConverter; + /** + * Policy that defines how new values are applied, be default it is applied initially once, but never updated + * anymore. + */ + private UpdatePolicy updatePolicy = UpdatePolicy.NEVER; + /** + * The current value, never null. + */ + private transient Optional<T> value; + /** + * The new value, or null. + */ + private transient Optional<T> newValue; + /** + * List of listeners that listen for changes. + */ + private transient WeakList<Consumer<PropertyChangeEvent>> listeners; + + /** + * Constructor. + * + * @param propertyName the name of the fields' property/method. + * @param keys the keys of the property, not null. + * @param configuration the configuration, not null. + * @param targetType the target type, not null. + * @param propertyConverter the optional converter to be used. + */ + DefaultDynamicValue(String propertyName, Configuration configuration, TypeLiteral<T> targetType, + PropertyConverter<T> propertyConverter, List<String> keys) { + this.propertyName = Objects.requireNonNull(propertyName); + this.keys = keys.toArray(new String[keys.size()]); + this.configuration = Objects.requireNonNull(configuration); + this.propertyConverter = propertyConverter; + this.targetType = targetType; + this.value = Optional.ofNullable(evaluateValue()); + } + + + /** + * Performs a commit, if necessary, and returns the current value. + * + * @return the non-null value held by this {@code DynamicValue} + * @throws org.apache.tamaya.ConfigException if there is no value present + * @see DefaultDynamicValue#isPresent() + */ + public T commitAndGet() { + commit(); + return get(); + } + + /** + * Commits a new value that has not been committed yet, make it the new value of the instance. On change any + * registered listeners will be triggered. + */ + public void commit() { + synchronized (this) { + if (newValue != null) { + PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, value.orElse(null), + newValue.orElse(null)); + value = newValue; + newValue = null; + for (Consumer<PropertyChangeEvent> consumer : listeners.get()) { + consumer.accept(evt); + } + } + } + } + + /** + * Discards a new value that was published. No listeners will be informed. + */ + public void discard() { + newValue = null; + } + + + /** + * Access the {@link UpdatePolicy} used for updating this value. + * + * @return the update policy, never null. + */ + public UpdatePolicy getUpdatePolicy() { + return updatePolicy; + } + + /** + * Sets a new {@link UpdatePolicy}. + * + * @param updatePolicy the new policy, not null. + */ + public void setUpdatePolicy(UpdatePolicy updatePolicy) { + this.updatePolicy = Objects.requireNonNull(updatePolicy); + } + + /** + * Add a listener to be called as weak reference, when this value has been changed. + * + * @param l the listener, not null + */ + public void addListener(Consumer<PropertyChangeEvent> l) { + if (listeners == null) { + listeners = new WeakList<>(); + } + listeners.add(l); + } + + /** + * Removes a listener to be called, when this value has been changed. + * + * @param l the listner to be removed, not null + */ + public void removeListener(Consumer<PropertyChangeEvent> l) { + if (listeners != null) { + listeners.remove(l); + } + } + + /** + * If a value is present in this {@code DynamicValue}, returns the value, + * otherwise throws {@code ConfigException}. + * + * @return the non-null value held by this {@code Optional} + * @throws org.apache.tamaya.ConfigException if there is no value present + * @see DefaultDynamicValue#isPresent() + */ + public T get() { + return value.get(); + } + + /** + * Method to check for and apply a new value. Depending on the {@link UpdatePolicy} + * the value is immediately or deferred visible (or it may even be ignored completely). + * + * @return true, if a new value has been detected. The value may not be visible depending on the current + * {@link DefaultDynamicValue.UpdatePolicy} in place. + */ + public boolean updateValue() { + T newValue = evaluateValue(); + if (Objects.equals(newValue, this.value.orElse(null))) { + return false; + } + switch (this.updatePolicy) { + case IMMEDIATE: + this.newValue = Optional.ofNullable(newValue); + commit(); + break; + case LOG_AND_DISCARD: + Logger.getLogger(getClass().getName()).info("Discard change on " + this + ", newValue=" + newValue); + this.newValue = null; + break; + case NEVER: + this.newValue = null; + break; + case EXPLCIT: + default: + this.newValue = Optional.ofNullable(newValue); + break; + } + return true; + } + + /** + * Evaluates the current value dynamically from the underlying configuration. + * + * @return the current actual value, or null. + */ + public T evaluateValue() { + for (String key : keys) { + T value; + if (propertyConverter == null) { + value = configuration.get(key, targetType); + } else { + value = configuration.get(key, propertyConverter); + } + if (value != null) { + return value; + } + } + return null; + } + + /** + * Access a new value that has not yet been committed. + * + * @return the uncommitted new value, or null. + */ + public T getNewValue() { + Optional<T> nv = newValue; + if (nv != null) { + return nv.orElse(null); + } + return null; + } + + /** + * Return {@code true} if there is a value present, otherwise {@code false}. + * + * @return {@code true} if there is a value present, otherwise {@code false} + */ + public boolean isPresent() { + return value.isPresent(); + } + + /** + * If a value is present, invoke the specified consumer with the value, + * otherwise do nothing. + * + * @param consumer block to be executed if a value is present + * @throws NullPointerException if value is present and {@code consumer} is + * null + */ + public void ifPresent(Consumer<? super T> consumer) { + value.ifPresent(consumer); + } + + /** + * Return the value if present, otherwise return {@code other}. + * + * @param other the value to be returned if there is no value present, may + * be null + * @return the value, if present, otherwise {@code other} + */ + public T orElse(T other) { + return value.orElse(other); + } + + /** + * Return the value if present, otherwise invoke {@code other} and return + * the result of that invocation. + * + * @param other a {@code Supplier} whose result is returned if no value + * is present + * @return the value if present otherwise the result of {@code other.get()} + * @throws NullPointerException if value is not present and {@code other} is + * null + */ + public T orElseGet(Supplier<? extends T> other) { + return value.orElseGet(other); + } + + /** + * Return the contained value, if present, otherwise throw an exception + * to be created by the provided supplier. + * <p> + * NOTE A method reference to the exception constructor with an empty + * argument list can be used as the supplier. For example, + * {@code IllegalStateException::new} + * + * @param <X> Type of the exception to be thrown + * @param exceptionSupplier The supplier which will return the exception to + * be thrown + * @return the present value + * @throws X if there is no value present + * @throws NullPointerException if no value is present and + * {@code exceptionSupplier} is null + */ + public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { + return value.orElseThrow(exceptionSupplier); + } + + /** + * Converts the instance to an {@link java.util.Optional} instance. + * + * @return the corresponding Optional value. + */ + public Optional<T> toOptional() { + return value; + } + + /** + * Serialization implementation that strips away the non serializable Optional part. + * + * @param oos the output stream + * @throws java.io.IOException if serialization fails. + */ + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.writeObject(updatePolicy); + if (isPresent()) { + oos.writeObject(this.value.get()); + } else { + oos.writeObject(null); + } + } + + /** + * Reads an instance from the input stream. + * + * @param ois the object input stream + * @throws java.io.IOException if deserialization fails. + * @throws ClassNotFoundException + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + this.updatePolicy = (UpdatePolicy) ois.readObject(); + if (isPresent()) { + this.value = Optional.of((T) ois.readObject()); + } + newValue = null; + } + + + /** + * Simple helper that allows keeping the listeners registered as weak references, hereby avoiding any + * memory leaks. + * + * @param <I> the type + */ + private class WeakList<I> { + final List<WeakReference<I>> refs = new LinkedList<>(); + + /** + * Adds a new instance. + * + * @param t the new instance, not null. + */ + void add(I t) { + refs.add(new WeakReference<>(t)); + } + + /** + * Removes a instance. + * + * @param t the instance to be removed. + */ + void remove(I t) { + synchronized (refs) { + for (Iterator<WeakReference<I>> iterator = refs.iterator(); iterator.hasNext(); ) { + WeakReference<I> ref = iterator.next(); + I instance = ref.get(); + if (instance == null || instance == t) { + iterator.remove(); + break; + } + } + } + } + + + /** + * Access a list (copy) of the current instances that were not discarded by the GC. + * + * @return the list of accessible items. + */ + public List<I> get() { + synchronized (refs) { + List<I> res = new ArrayList<>(); + for (Iterator<WeakReference<I>> iterator = refs.iterator(); iterator.hasNext(); ) { + WeakReference<I> ref = iterator.next(); + I instance = ref.get(); + if (instance == null) { + iterator.remove(); + } else { + res.add(instance); + } + } + return res; + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/main/java/org/apache/tamaya/inject/internal/InjectionUtils.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/InjectionUtils.java b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/InjectionUtils.java index 765331b..fae86b5 100644 --- a/modules/injection/src/main/java/org/apache/tamaya/inject/internal/InjectionUtils.java +++ b/modules/injection/src/main/java/org/apache/tamaya/inject/internal/InjectionUtils.java @@ -74,6 +74,9 @@ final class InjectionUtils { * @return the list current keys in order how they should be processed/looked up. */ public static List<String> evaluateKeys(Member member, ConfigRoot areasAnnot, ConfiguredProperty propertyAnnotation) { + if(propertyAnnotation==null){ + return evaluateKeys(member, areasAnnot); + } List<String> keys = new ArrayList<>(Arrays.asList(propertyAnnotation.keys())); if (keys.isEmpty()) { keys.add(member.getName()); @@ -159,19 +162,41 @@ final class InjectionUtils { ConfiguredProperty prop = element.getAnnotation(ConfiguredProperty.class); DefaultValue defaultAnnot = element.getAnnotation(DefaultValue.class); String configValue = null; + List<String> keys = null; if (prop == null) { - List<String> keys = InjectionUtils.evaluateKeys((Member) element, areasAnnot); - configValue = evaluteConfigValue(keys); + keys = InjectionUtils.evaluateKeys((Member) element, areasAnnot); } else { - List<String> keys = InjectionUtils.evaluateKeys((Member) element, areasAnnot, prop); - configValue = evaluteConfigValue(keys); + keys = InjectionUtils.evaluateKeys((Member) element, areasAnnot, prop); } + configValue = evaluteConfigValue(keys); if (configValue == null && defaultAnnot != null) { return defaultAnnot.value(); } return configValue; } + /** + * Collects all keys to be be accessed as defined by any annotations of type + * {@link org.apache.tamaya.inject.ConfigRoot}, {@link org.apache.tamaya.inject.ConfiguredProperty}. + * @param field the (optionally) annotated field instance + * @return the regarding key list to be accessed fomr the {@link org.apache.tamaya.Configuration}. + */ + public static List<String> getKeys(Field field) { + ConfigRoot areasAnnot = field.getDeclaringClass().getAnnotation(ConfigRoot.class); + return InjectionUtils.evaluateKeys((Member) field, areasAnnot, field.getAnnotation(ConfiguredProperty.class)); + } + + /** + * Collects all keys to be be accessed as defined by any annotations of type + * {@link org.apache.tamaya.inject.ConfigRoot}, {@link org.apache.tamaya.inject.ConfiguredProperty}. + * @param method the (optionally) annotated method instance + * @return the regarding key list to be accessed fomr the {@link org.apache.tamaya.Configuration}. + */ + public static List<String> getKeys(Method method) { + ConfigRoot areasAnnot = method.getDeclaringClass().getAnnotation(ConfigRoot.class); + return InjectionUtils.evaluateKeys((Member) method, areasAnnot, method.getAnnotation(ConfiguredProperty.class)); + } + private static String evaluteConfigValue(List<String> keys) { String configValue = null; for (String key : keys) { @@ -235,4 +260,6 @@ final class InjectionUtils { } return value; } + + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/test/java/annottext/AnnotatedConfigBean.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/test/java/annottext/AnnotatedConfigBean.java b/modules/injection/src/test/java/annottext/AnnotatedConfigBean.java index c1eb39c..94eb570 100644 --- a/modules/injection/src/test/java/annottext/AnnotatedConfigBean.java +++ b/modules/injection/src/test/java/annottext/AnnotatedConfigBean.java @@ -20,6 +20,7 @@ package annottext; import org.apache.tamaya.inject.ConfiguredProperty; import org.apache.tamaya.inject.DefaultValue; +import org.apache.tamaya.inject.DynamicValue; import org.apache.tamaya.inject.LoadPolicy; import org.apache.tamaya.inject.WithLoadPolicy; @@ -46,6 +47,9 @@ public class AnnotatedConfigBean { @ConfiguredProperty(keys = "host.name") private String hostName; + @ConfiguredProperty(keys = "host.name") + private DynamicValue<String> dynamicHostname; + public String getAnotherValue(){ return anotherValue; } @@ -54,4 +58,8 @@ public class AnnotatedConfigBean { return hostName; } + public DynamicValue<String> getDynamicValue(){ + return dynamicHostname; + } + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/test/java/annottext/AnnotatedConfigTemplate.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/test/java/annottext/AnnotatedConfigTemplate.java b/modules/injection/src/test/java/annottext/AnnotatedConfigTemplate.java index 8aa3f54..94fc188 100644 --- a/modules/injection/src/test/java/annottext/AnnotatedConfigTemplate.java +++ b/modules/injection/src/test/java/annottext/AnnotatedConfigTemplate.java @@ -20,6 +20,7 @@ package annottext; import org.apache.tamaya.inject.ConfiguredProperty; import org.apache.tamaya.inject.DefaultValue; +import org.apache.tamaya.inject.DynamicValue; import org.apache.tamaya.inject.LoadPolicy; import org.apache.tamaya.inject.WithLoadPolicy; @@ -32,7 +33,7 @@ import org.apache.tamaya.inject.WithLoadPolicy; public interface AnnotatedConfigTemplate { @ConfiguredProperty(keys = {"foo.bar.myprop", "mp","common.testdata.myProperty"}) - @DefaultValue("myValue_${env.stage}") + @DefaultValue("ET") // @ConfigLoadPolicy(listener = MyListener.class) String myParameter(); @@ -41,9 +42,12 @@ public interface AnnotatedConfigTemplate { String simpleValue(); @ConfiguredProperty - String simplestValue(); + default String simplestValue(){return "HALLO!";} @ConfiguredProperty(keys = "host.name") String hostName(); + @ConfiguredProperty(keys = "host.name") + DynamicValue<String> getDynamicValue(); + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/4ddf46f7/modules/injection/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java ---------------------------------------------------------------------- diff --git a/modules/injection/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java b/modules/injection/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java index 654e97a..aaed64b 100644 --- a/modules/injection/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java +++ b/modules/injection/src/test/java/org/apache/tamaya/inject/TamayaInjectionTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * Created by Anatole on 12.01.2015. @@ -43,6 +44,25 @@ public class TamayaInjectionTest { assertEquals(testInstance.getAnotherValue(), "HALLO!"); 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.getHostName(), testInstance.getDynamicValue().get()); + } + + @Test + public void testConfigTemplate(){ + assertNotNull(ConfigurationInjector.getInstance()); + AnnotatedConfigTemplate testInstance = ConfigurationInjector.getInstance() + .createTemplate(AnnotatedConfigTemplate.class); + assertEquals(testInstance.hostName(), "tamaya01.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.hostName(), testInstance.getDynamicValue().get()); +// assertEquals(testInstance.simplestValue(), "HALLO!"); } }