http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java ---------------------------------------------------------------------- diff --git a/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java b/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java index d044982..43c6248 100644 --- a/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java +++ b/api/src/test/java/org/apache/tamaya/TestConfigServiceSingletonSpi.java @@ -36,53 +36,54 @@ public class TestConfigServiceSingletonSpi implements ConfigurationManagerSingle public TestConfigServiceSingletonSpi(){ Map<String,String> config = new HashMap<>(); - config.put("a.b.c.key1", "value current a.b.c.key1"); - config.put("a.b.c.key2", "value current a.b.c.key2"); - config.put("a.b.key3", "value current a.b.key3"); - config.put("a.b.key4", "value current a.b.key4"); - config.put("a.key5", "value current a.key5"); - config.put("a.key6", "value current a.key6"); + config.put("a.b.c.key1", "keys current a.b.c.key1"); + config.put("a.b.c.key2", "keys current a.b.c.key2"); + config.put("a.b.key3", "keys current a.b.key3"); + config.put("a.b.key4", "keys current a.b.key4"); + config.put("a.key5", "keys current a.key5"); + config.put("a.key6", "keys current a.key6"); config.put("int1", "123456"); config.put("int2", "111222"); config.put("booleanT", "true"); config.put("double1", "1234.5678"); config.put("BD", "123456789123456789123456789123456789.123456789123456789123456789123456789"); - config.put("testProperty", "value current testProperty"); + config.put("testProperty", "keys current testProperty"); config.put("runtimeVersion", "${java.version}"); // configs.put("test", new MapConfiguration(MetaInfoBuilder.current().setName("test").build(), config)); } + + @Override public boolean isConfigurationDefined(String name){ return configs.containsKey(name); } @Override - public <T> T getConfiguration(String name, Class<T> type){ - if(type.equals(Configuration.class)) { - Configuration config = configs.get(name); - return (T)Optional.ofNullable(config).orElseThrow(() -> new ConfigException("No such config: " + name)); - } - throw new ConfigException("Not such config name="+name+", type="+ type.getName()); + public Configuration getConfiguration(String name) { + // TODO + throw new UnsupportedOperationException("Not yet implemented"); } @Override - public <T> T getConfiguration(Class<T> type) { + public <T> T createTemplate(Class<T> type, Configuration... configurations) { // TODO throw new UnsupportedOperationException("Not yet implemented"); } @Override - public void configure(Object instance) { + public void configure(Object instance, Configuration... configurations) { // TODO throw new UnsupportedOperationException("Not yet implemented"); } @Override - public String evaluateValue(Configuration config, String expression){ + public String evaluateValue(String expression, Configuration... configurations) { // TODO improve this ugly implementation... - for(Map.Entry<String, String> en: config.toMap().entrySet()){ - expression = expression.replaceAll("\\$\\{"+en.getKey()+"\\}", en.getValue()); + for (Configuration config : configurations) { + for (Map.Entry<String, String> en : config.toMap().entrySet()) { + expression = expression.replaceAll("\\$\\{" + en.getKey() + "\\}", en.getValue()); + } } return expression; }
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java ---------------------------------------------------------------------- diff --git a/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java b/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java index 2d3cfbc..b05c356 100644 --- a/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java +++ b/api/src/test/java/org/apache/tamaya/TestPropertyAdaptersSingletonSpi.java @@ -18,8 +18,9 @@ */ package org.apache.tamaya; -import org.apache.tamaya.annotation.WithPropertyAdapter; -import org.apache.tamaya.spi.PropertyAdaptersSingletonSpi; +import org.apache.tamaya.annotation.WithCodec; +import org.apache.tamaya.spi.CodecsSingletonSpi; + import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; @@ -32,66 +33,66 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; /** - * Test implementation current {@link PropertyAdaptersSingletonSpi}, which provides adapters + * Test implementation current {@link org.apache.tamaya.spi.CodecsSingletonSpi}, which provides codecs * for some basic types. */ -public final class TestPropertyAdaptersSingletonSpi implements PropertyAdaptersSingletonSpi{ +public final class TestPropertyAdaptersSingletonSpi implements CodecsSingletonSpi { - private Map<Class, PropertyAdapter<?>> adapters = new ConcurrentHashMap<>(); + private Map<Class, Codec<?>> codecs = new ConcurrentHashMap<>(); private TestPropertyAdaptersSingletonSpi(){ - register(char.class, (s) -> s.charAt(0)); - register(int.class, Integer::parseInt); - register(byte.class, Byte::parseByte); - register(short.class, Short::parseShort); - register(boolean.class, Boolean::parseBoolean); - register(float.class, Float::parseFloat); - register(double.class, Double::parseDouble); + register(char.class, (s) -> s.charAt(0), (ch) -> String.valueOf(ch)); + register(int.class, Integer::parseInt, Object::toString); + register(byte.class, Byte::parseByte, Object::toString); + register(short.class, Short::parseShort, Object::toString); + register(boolean.class, Boolean::parseBoolean, b -> String.valueOf(b)); + register(float.class, Float::parseFloat, f -> String.valueOf(f)); + register(double.class, Double::parseDouble, d -> String.valueOf(d)); - register(Character.class, (s) -> s.charAt(0)); - register(Integer.class, Integer::parseInt); - register(Byte.class, Byte::parseByte); - register(Short.class, Short::parseShort); - register(Boolean.class, Boolean::parseBoolean); - register(Float.class, Float::parseFloat); - register(Double.class, Double::parseDouble); - register(BigDecimal.class, BigDecimal::new); - register(BigInteger.class, BigInteger::new); + register(Character.class, (s) -> s.charAt(0), Object::toString); + register(Integer.class, Integer::valueOf, Object::toString); + register(Byte.class, Byte::valueOf, Object::toString); + register(Short.class, Short::valueOf, String::valueOf); + register(Boolean.class, Boolean::valueOf, String::valueOf); + register(Float.class, Float::valueOf, String::valueOf); + register(Double.class, Double::valueOf, String::valueOf); + register(BigDecimal.class, BigDecimal::new, String::valueOf); + register(BigInteger.class, BigInteger::new, String::valueOf); - register(Currency.class, Currency::getInstance); + register(Currency.class, Currency::getInstance, Object::toString); - register(LocalDate.class, LocalDate::parse); - register(LocalTime.class, LocalTime::parse); - register(LocalDateTime.class, LocalDateTime::parse); - register(ZoneId.class, ZoneId::of); + register(LocalDate.class, LocalDate::parse, Object::toString); + register(LocalTime.class, LocalTime::parse, Object::toString); + register(LocalDateTime.class, LocalDateTime::parse, Object::toString); + register(ZoneId.class, ZoneId::of, ZoneId::getId); } @Override - public <T> PropertyAdapter<T> register(Class<T> targetType, PropertyAdapter<T> adapter){ + public <T> Codec<T> register(Class<T> targetType, Codec<T> codec){ Objects.requireNonNull(targetType); - Objects.requireNonNull(adapter); - return (PropertyAdapter<T>)adapters.put(targetType, adapter); + Objects.requireNonNull(codec); + return (Codec<T>) codecs.put(targetType, codec); } @Override - public <T> PropertyAdapter<T> getAdapter(Class<T> targetType, WithPropertyAdapter annotation){ + public <T> Codec<T> getCodec(Class<T> targetType, WithCodec annotation){ if(annotation!=null){ Class<?> adapterType = annotation.value(); - if(!adapterType.equals(PropertyAdapter.class)){ + if(!adapterType.equals(Codec.class)){ try{ - return (PropertyAdapter<T>)adapterType.newInstance(); + return (Codec<T>)adapterType.newInstance(); } catch(Exception e){ throw new ConfigException("Failed to load PropertyAdapter: " + adapterType, e); } } } - return (PropertyAdapter<T>) adapters.get(targetType); + return (Codec<T>) codecs.get(targetType); } @Override public boolean isTargetTypeSupported(Class<?> targetType){ - return adapters.containsKey(targetType); + return codecs.containsKey(targetType); } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecsSingletonSpi ---------------------------------------------------------------------- diff --git a/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecsSingletonSpi b/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecsSingletonSpi new file mode 100644 index 0000000..e9b04b4 --- /dev/null +++ b/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.CodecsSingletonSpi @@ -0,0 +1,19 @@ +# +# 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 current 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. +# +org.apache.tamaya.TestPropertyAdaptersSingletonSpi http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdaptersSingletonSpi ---------------------------------------------------------------------- diff --git a/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdaptersSingletonSpi b/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdaptersSingletonSpi deleted file mode 100644 index e9b04b4..0000000 --- a/api/src/test/resources/META-INF/services/org.apache.tamaya.spi.PropertyAdaptersSingletonSpi +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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 current 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. -# -org.apache.tamaya.TestPropertyAdaptersSingletonSpi http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/config/AbstractConfiguration.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/config/AbstractConfiguration.java b/core/src/main/java/org/apache/tamaya/core/config/AbstractConfiguration.java index cfd0da3..836db3f 100644 --- a/core/src/main/java/org/apache/tamaya/core/config/AbstractConfiguration.java +++ b/core/src/main/java/org/apache/tamaya/core/config/AbstractConfiguration.java @@ -48,10 +48,10 @@ public abstract class AbstractConfiguration extends AbstractPropertySource imple @Override public <T> Optional<T> get(String key, Class<T> type){ AdapterProviderSpi as = ServiceContext.getInstance().getSingleton(AdapterProviderSpi.class); - PropertyAdapter<T> adapter = as.getAdapter(type); + Codec<T> adapter = as.getAdapter(type); if(adapter == null){ throw new ConfigException( - "Can not adapt config property '" + key + "' to " + type.getName() + ": no such " + + "Can not deserialize config property '" + key + "' to " + type.getName() + ": no such " + "adapter."); } return getAdapted(key, adapter); http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/config/ConfigFunctions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/config/ConfigFunctions.java b/core/src/main/java/org/apache/tamaya/core/config/ConfigFunctions.java index c9d924f..ddd83e2 100644 --- a/core/src/main/java/org/apache/tamaya/core/config/ConfigFunctions.java +++ b/core/src/main/java/org/apache/tamaya/core/config/ConfigFunctions.java @@ -132,7 +132,7 @@ public final class ConfigFunctions { /** * Creates a {@link UnaryOperator} that creates a {@link org.apache.tamaya.Configuration} that maps any keys as * defined by the {@code keyMapper} given. If the {@code keyMapper} returns - * {@code null} for a value, it is removed from the resulting map. + * {@code null} for a keys, it is removed from the resulting map. * * @param keyMapper the key mapper, not null * @return the area configuration, with the areaKey stripped away. http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java b/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java index 3355e6b..176cc38 100644 --- a/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java +++ b/core/src/main/java/org/apache/tamaya/core/config/ConfigurationBuilder.java @@ -108,7 +108,7 @@ public final class ConfigurationBuilder { /** * Sets the aggregation policy to be used, when adding additional property sets. The policy will - * be active a slong as the builder is used or it is reset to another value. + * be active a slong as the builder is used or it is reset to another keys. * * @param aggregationPolicy the aggregation policy, not null. * @return the builder for chaining. @@ -341,7 +341,7 @@ public final class ConfigurationBuilder { * created. * * @param key the key to be added, not null. - * @param value the value to be added, not null. + * @param value the keys to be added, not null. * @return this builder for chaining */ public ConfigurationBuilder setMeta(String key, String value){ http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/config/MappedConfiguration.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/config/MappedConfiguration.java b/core/src/main/java/org/apache/tamaya/core/config/MappedConfiguration.java index 24c715e..8d1207f 100644 --- a/core/src/main/java/org/apache/tamaya/core/config/MappedConfiguration.java +++ b/core/src/main/java/org/apache/tamaya/core/config/MappedConfiguration.java @@ -3,8 +3,6 @@ package org.apache.tamaya.core.config; import org.apache.tamaya.*; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; import java.util.function.UnaryOperator; /** @@ -46,8 +44,8 @@ class MappedConfiguration extends AbstractConfiguration implements Configuration } @Override - public void apply(ConfigChangeSet change) { - this.config.apply(change); + public void applyChanges(ConfigChangeSet change) { + this.config.applyChanges(change); } @Override http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/env/EnvironmentBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/env/EnvironmentBuilder.java b/core/src/main/java/org/apache/tamaya/core/env/EnvironmentBuilder.java index 7445430..895ac76 100644 --- a/core/src/main/java/org/apache/tamaya/core/env/EnvironmentBuilder.java +++ b/core/src/main/java/org/apache/tamaya/core/env/EnvironmentBuilder.java @@ -52,7 +52,7 @@ public final class EnvironmentBuilder{ /** * Sets a new environment property. * @param key the key, not null. - * @param value the value, not null. + * @param value the keys, not null. * @return the builder for chaining */ public EnvironmentBuilder set(String key, String value){ http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/Utils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/Utils.java b/core/src/main/java/org/apache/tamaya/core/internal/Utils.java index 6990de2..0089b8f 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/Utils.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/Utils.java @@ -57,7 +57,7 @@ public final class Utils { if(containerAnnot!=null){ Method valueMethod; try { - valueMethod = annotationContainer.getMethod("value"); + valueMethod = annotationContainer.getMethod("keys"); result.addAll(Arrays.asList((T[])valueMethod.invoke(containerAnnot))); } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to evaluate repeatable annotation.", e); @@ -90,7 +90,7 @@ public final class Utils { if(containerAnnot!=null){ Method valueMethod; try { - valueMethod = annotationContainer.getMethod("value"); + valueMethod = annotationContainer.getMethod("keys"); result.addAll(Arrays.asList((T[])valueMethod.invoke(containerAnnot))); } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to evaluate repeatable annotation.", e); http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/config/ConfigTemplateInvocationHandler.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/ConfigTemplateInvocationHandler.java b/core/src/main/java/org/apache/tamaya/core/internal/config/ConfigTemplateInvocationHandler.java deleted file mode 100644 index 18e9d03..0000000 --- a/core/src/main/java/org/apache/tamaya/core/internal/config/ConfigTemplateInvocationHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.core.internal.config; - -import org.apache.tamaya.Configuration; -import org.apache.tamaya.core.internal.inject.ConfiguredType; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.util.Objects; - -/** - * Invocation handler that handles request against a configuration template. - */ -class ConfigTemplateInvocationHandler implements InvocationHandler { - /** The underlying configuration. */ - private Configuration config; - /** The configured type. */ - private ConfiguredType type; - - public ConfigTemplateInvocationHandler(Class<?> type, Configuration config) { - this.config = Objects.requireNonNull(config); - this.type = new ConfiguredType(Objects.requireNonNull(type)); - if(!type.isInterface()){ - throw new IllegalArgumentException("Can only proxy interfaces as configuration templates."); - } - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if("toString".equals(method.getName())){ - return "Configured Proxy -> " + this.type.getType().getName(); - } - return this.type.getConfiguredValue(method, args); - } -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultConfigurationManagerSingletonSpi.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultConfigurationManagerSingletonSpi.java b/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultConfigurationManagerSingletonSpi.java index 1a04cef..e3e0bba 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultConfigurationManagerSingletonSpi.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/config/DefaultConfigurationManagerSingletonSpi.java @@ -20,6 +20,7 @@ package org.apache.tamaya.core.internal.config; import org.apache.tamaya.*; import org.apache.tamaya.core.internal.el.DefaultExpressionEvaluator; +import org.apache.tamaya.core.internal.inject.ConfigTemplateInvocationHandler; import org.apache.tamaya.core.internal.inject.ConfigurationInjector; import org.apache.tamaya.core.properties.PropertySourceBuilder; import org.apache.tamaya.core.spi.ConfigurationProviderSpi; @@ -64,43 +65,21 @@ public class DefaultConfigurationManagerSingletonSpi implements ConfigurationMan } @Override - public <T> T getConfiguration(String name, Class<T> type) { - ConfigurationProviderSpi provider = configProviders.get(name); - if (provider == null) { - if (DEFAULT_CONFIG_NAME.equals(name)) { - provider = new FallbackSimpleConfigProvider(); - configProviders.put(DEFAULT_CONFIG_NAME, provider); - } else { - throw new ConfigException("No such config: " + name); - } - } - Configuration config = provider.getConfiguration(); - if (config == null) { - throw new ConfigException("No such config: " + name); - } - if (Configuration.class.equals(type)) { - return (T) config; - } - return createAdapterProxy(config, type); + public <T> T createTemplate(Class<T> type, Configuration... configurations) { + ClassLoader cl = Optional.ofNullable(Thread.currentThread() + .getContextClassLoader()).orElse(getClass().getClassLoader()); + return (T) Proxy.newProxyInstance(cl, new Class[]{type}, new ConfigTemplateInvocationHandler(type, configurations)); } /** - * Creates a proxy implementing the given target interface. * - * @param config the configuration to be used for providing values. - * @param type the target interface. - * @param <T> the target interface type. - * @return the corresponding implementing proxy, never null. + * @param instance the instance with configuration annotations, not null. + * @param configurations the configurations to be used for evaluating the values for injection into {@code instance}. + * If no items are passed, the default configuration is used. */ - private <T> T createAdapterProxy(Configuration config, Class<T> type) { - ClassLoader cl = Optional.ofNullable(Thread.currentThread() - .getContextClassLoader()).orElse(getClass().getClassLoader()); - return (T) Proxy.newProxyInstance(cl, new Class[]{type}, new ConfigTemplateInvocationHandler(type, config)); - } - @Override - public void configure(Object instance) { - ConfigurationInjector.configure(instance); + public void configure(Object instance, Configuration... configurations) { + ConfigurationInjector.configure(instance, configurations); } private String getConfigId(Annotation... qualifiers) { @@ -119,8 +98,8 @@ public class DefaultConfigurationManagerSingletonSpi implements ConfigurationMan } @Override - public String evaluateValue(Configuration config, String expression) { - return expressionEvaluator.evaluate(expression); + public String evaluateValue(String expression, Configuration... configurations) { + return expressionEvaluator.evaluate(expression, configurations); } @Override @@ -162,9 +141,34 @@ public class DefaultConfigurationManagerSingletonSpi implements ConfigurationMan return spi != null; } + @Override + public Configuration getConfiguration(String name) { + ConfigurationProviderSpi provider = configProviders.get(name); + if (provider == null) { + if (DEFAULT_CONFIG_NAME.equals(name)) { + provider = new FallbackSimpleConfigProvider(); + configProviders.put(DEFAULT_CONFIG_NAME, provider); + } else { + throw new ConfigException("No such config: " + name); + } + } + Configuration config = provider.getConfiguration(); + if (config == null) { + throw new ConfigException("No such config: " + name); + } + return config; + } + /** * Implementation of a default config provider used as fallback, if no {@link org.apache.tamaya.core.spi.ConfigurationProviderSpi} - * instance is registered for providing the {@code default} {@link org.apache.tamaya.Configuration}. + * instance is registered for providing the {@code default} {@link org.apache.tamaya.Configuration}. The providers loads the follwing + * config resources: + * <ul> + * <li>Classpath: META-INF/cfg/default/**/*.xml, META-INF/cfg/default/**/*.properties, META-INF/cfg/default/**/*.ini</li> + * <li>Classpath: META-INF/cfg/config/#42;#42;/#42;.xml, META-INF/cfg/config/#42;#42;/#42;.properties, META-INF/cfg/config/#42;#42;/#42;.ini</li> + * <li>Files: defined by the system property -Dconfig.dir</li> + * <li>system properties</li> + * </ul> */ private static final class FallbackSimpleConfigProvider implements ConfigurationProviderSpi { /** @@ -190,19 +194,22 @@ public class DefaultConfigurationManagerSingletonSpi implements ConfigurationMan @Override public void reload() { - this.configuration = + PropertySourceBuilder builder = PropertySourceBuilder.of(DEFAULT_CONFIG_NAME) .addProviders(PropertySourceBuilder.of("CL default") .withAggregationPolicy(AggregationPolicy.LOG_ERROR) - .addPaths("META-INF/cfg/default/**/*.xml", "META-INF/cfg/default/**/*.properties", "META-INF/cfg/default/**/*.ini") + .addPaths("classpath:META-INF/cfg/default/**/*.xml", "classpath:META-INF/cfg/default/**/*.properties", "classpath:META-INF/cfg/default/**/*.ini") .build()) .addProviders(PropertySourceBuilder.of("CL default") .withAggregationPolicy(AggregationPolicy.LOG_ERROR) - .addPaths("META-INF/cfg/config/**/*.xml", "META-INF/cfg/config/**/*.properties", "META-INF/cfg/config/**/*.ini") - .build()) - .addSystemProperties() - .addEnvironmentProperties() - .build().toConfiguration(); + .addPaths("classpath:META-INF/cfg/config/**/*.xml", "classpath:META-INF/cfg/config/**/*.properties", "classpath:META-INF/cfg/config/**/*.ini") + .build()); + String configDir = System.getProperty("config.dir"); + if(configDir!=null && !configDir.trim().isEmpty()){ + builder.addPaths("file:"+configDir); + } + builder.addSystemProperties(); + this.configuration = builder.build().toConfiguration(); } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/el/DefaultExpressionEvaluator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/el/DefaultExpressionEvaluator.java b/core/src/main/java/org/apache/tamaya/core/internal/el/DefaultExpressionEvaluator.java index e5baecc..457aa6c 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/el/DefaultExpressionEvaluator.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/el/DefaultExpressionEvaluator.java @@ -19,6 +19,7 @@ package org.apache.tamaya.core.internal.el; import org.apache.tamaya.ConfigException; +import org.apache.tamaya.Configuration; import org.apache.tamaya.spi.ServiceContext; import org.apache.tamaya.core.spi.ExpressionEvaluator; import org.apache.tamaya.core.spi.ExpressionResolver; @@ -27,7 +28,9 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** - * Created by Anatole on 28.09.2014. + * Default expression evaluator that manages several instances of {@link org.apache.tamaya.core.spi.ExpressionResolver}. + * Each resolver is identified by a resolver id. Each expression passed has the form resolverId:resolverExpression, which + * has the advantage that different resolvers can be active in parallel. */ public final class DefaultExpressionEvaluator implements ExpressionEvaluator{ @@ -61,11 +64,14 @@ public final class DefaultExpressionEvaluator implements ExpressionEvaluator{ * </ul> * * @param expression the expression to be evaluated, not null + * @param configurations overriding configurations to be used for evaluating the values for injection into {@code instance}. + * If no such config is passed, the default configurations provided by the current + * registered providers are used. * @return the evaluated expression. * @throws org.apache.tamaya.ConfigException if resolution fails. */ @Override - public String evaluate(String expression) { + public String evaluate(String expression, Configuration... configurations) { StringTokenizer tokenizer = new StringTokenizer(expression, "${}\\", true); boolean escaped = false; StringBuilder resolvedValue = new StringBuilder(); @@ -106,7 +112,7 @@ public final class DefaultExpressionEvaluator implements ExpressionEvaluator{ if (!"}".equals(tokenizer.nextToken())) { throw new ConfigException("Invalid expression encountered: " + expression); } - // evalute subexpression + // evaluate sub-expression current.append(evaluteInternal(subExpression)); break; default: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/el/EnvironmentPropertyResolver.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/el/EnvironmentPropertyResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/el/EnvironmentPropertyResolver.java index 4aa41ab..68d37a4 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/el/EnvironmentPropertyResolver.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/el/EnvironmentPropertyResolver.java @@ -19,12 +19,13 @@ package org.apache.tamaya.core.internal.el; import org.apache.tamaya.ConfigException; +import org.apache.tamaya.Configuration; import org.apache.tamaya.core.spi.ExpressionResolver; import java.util.Optional; /** - * Created by Anatole on 28.09.2014. + * Property resolver implementation that interprets the resolver expressions as environment properties. */ public final class EnvironmentPropertyResolver implements ExpressionResolver{ @@ -34,7 +35,7 @@ public final class EnvironmentPropertyResolver implements ExpressionResolver{ } @Override - public String resolve(String expression){ + public String resolve(String expression, Configuration... configurations){ return Optional.ofNullable(System.getenv(expression)).orElseThrow( () -> new ConfigException("No such environment property: " + expression) ); http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/el/SystemPropertyResolver.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/el/SystemPropertyResolver.java b/core/src/main/java/org/apache/tamaya/core/internal/el/SystemPropertyResolver.java index 450504e..ef1ffc6 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/el/SystemPropertyResolver.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/el/SystemPropertyResolver.java @@ -25,7 +25,7 @@ import org.apache.tamaya.core.spi.ExpressionResolver; import java.util.*; /** - * Created by Anatole on 28.09.2014. + * Property resolver implementation that interprets the resolver expression as system property name. */ public final class SystemPropertyResolver implements ExpressionResolver{ @@ -35,7 +35,7 @@ public final class SystemPropertyResolver implements ExpressionResolver{ } @Override - public String resolve(String expression){ + public String resolve(String expression, Configuration... configurations){ return Optional.ofNullable(System.getProperty(expression)).orElseThrow( () -> new ConfigException("No such system property: " + expression) ); http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigTemplateInvocationHandler.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigTemplateInvocationHandler.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigTemplateInvocationHandler.java new file mode 100644 index 0000000..70070c5 --- /dev/null +++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigTemplateInvocationHandler.java @@ -0,0 +1,65 @@ +/* + * 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.core.internal.inject; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.core.internal.inject.ConfiguredType; +import org.apache.tamaya.core.internal.inject.InjectionUtils; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Objects; + +/** + * Invocation handler that handles request against a configuration template. + */ +public final class ConfigTemplateInvocationHandler implements InvocationHandler { + /** + * Any overriding configurations. + */ + private Configuration[] configurations; + /** + * 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. + */ + public ConfigTemplateInvocationHandler(Class<?> type, Configuration... configurations) { + this.configurations = Objects.requireNonNull(configurations).clone(); + this.type = new ConfiguredType(Objects.requireNonNull(type)); + if (!type.isInterface()) { + throw new IllegalArgumentException("Can only proxy interfaces as configuration templates."); + } + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("toString".equals(method.getName())) { + return "Configured Proxy -> " + this.type.getType().getName(); + } + String configValue = InjectionUtils.getConfigValue(method, configurations); + return InjectionUtils.adaptValue(method, method.getReturnType(), configValue); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigurationInjector.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigurationInjector.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigurationInjector.java index 10d2374..026b4e0 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigurationInjector.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfigurationInjector.java @@ -18,6 +18,8 @@ */ package org.apache.tamaya.core.internal.inject; +import org.apache.tamaya.Configuration; + import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -31,6 +33,11 @@ public final class ConfigurationInjector { private Map<Class, ConfiguredType> configuredTypes = new ConcurrentHashMap<>(); + /** + * Extract the configuration annotation config and registers it per class, for later reuse. + * @param type the type to be configured. + * @return the configured type registered. + */ public static ConfiguredType registerType(Class<?> type){ if (!ConfiguredType.isConfigured(type)) { return null; @@ -38,20 +45,22 @@ public final class ConfigurationInjector { return INSTANCE.configuredTypes.computeIfAbsent(type, ConfiguredType::new); } - public static void configure(Object instance){ + /** + * Configured the current instance and reigsterd necessary listener to forward config change events as + * defined by the current annotations in place. + * @param instance the instance to be configured + * @param configurations Configuration instances that replace configuration served by services. This allows + * more easily testing and adaption. + */ + public static void configure(Object instance, Configuration... configurations){ Class type = Objects.requireNonNull(instance).getClass(); if (!ConfiguredType.isConfigured(type)) { throw new IllegalArgumentException("Not a configured type: " + type.getName()); } - ConfiguredType configType = registerType(type); - initializeConfiguredFields(configType, instance); + ConfiguredType configuredType = registerType(type); + Objects.requireNonNull(configuredType).configure(instance, configurations); } - private static <T> void initializeConfiguredFields(final ConfiguredType configuredType, Object instance) { - Objects.requireNonNull(configuredType).configure(instance); - ConfiguredInstancesManager.register(configuredType, instance); - - } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java index ee5aeda..581021b 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredField.java @@ -18,9 +18,9 @@ */ package org.apache.tamaya.core.internal.inject; +import org.apache.tamaya.Codec; import org.apache.tamaya.ConfigException; import org.apache.tamaya.Configuration; -import org.apache.tamaya.PropertyAdapter; import org.apache.tamaya.annotation.*; import org.apache.tamaya.core.internal.Utils; @@ -31,8 +31,8 @@ import java.util.stream.Collectors; /** * Small class that contains and manages all information anc access to a configured field and a concrete instance current - * it (referenced by a weak reference). It also implements all aspects current value filtering, converting any applying the - * final value by reflection. + * it (referenced by a weak reference). It also implements all aspects current keys filtering, converting any applying the + * final keys by reflection. */ @SuppressWarnings("UnusedDeclaration") public class ConfiguredField { @@ -55,133 +55,44 @@ public class ConfiguredField { } /** - * Evaluate the initial value fromMap the configuration and apply it to the field. + * Evaluate the initial keys fromMap the configuration and applyChanges it to the field. * * @param target the target instance. + * @param configurations Configuration instances that replace configuration served by services. This allows + * more easily testing and adaption. * @throws ConfigException if evaluation or conversion failed. */ - public void applyInitialValue(Object target) throws ConfigException { - Collection<ConfiguredProperty> configuredProperties = Utils.getAnnotations( - annotatedField, ConfiguredProperty.class, ConfiguredProperties.class); - DefaultAreas areasAnnot = this.annotatedField.getDeclaringClass().getAnnotation(DefaultAreas.class); - WithLoadPolicy loadPolicy = Utils.getAnnotation(WithLoadPolicy.class, this.annotatedField, this.annotatedField.getDeclaringClass()); - DefaultValue defaultValue = this.annotatedField.getAnnotation(DefaultValue.class); - String configValue = getConfigValue(loadPolicy, areasAnnot, configuredProperties, defaultValue); - applyValue(target, configValue, false); + public void applyInitialValue(Object target, Configuration... configurations) throws ConfigException { + String configValue = InjectionUtils.getConfigValue(this.annotatedField, configurations); + applyValue(target, configValue, false, configurations); } - /** - * Internally evaluated the current vaslid configuration value based on the given annotations present. - * - * @param loadPolicyAnnot The load policy, determining any explicit listeners to be informed. - * @param areasAnnot Any default areas to be looked up. - * @param propertiesAnnot The configured property keys (qualified or relative). - * @param defaultAnnot any configured default value. - * @return the value to be applied, or null. - */ - private String getConfigValue(WithLoadPolicy loadPolicyAnnot, DefaultAreas areasAnnot, Collection<ConfiguredProperty> propertiesAnnot, DefaultValue defaultAnnot) { - List<String> keys = evaluateKeys(areasAnnot, propertiesAnnot); - annotatedField.setAccessible(true); - Configuration config = getConfiguration(); - String configValue = null; - for (String key : keys) { - if (config.containsKey(key)) { - configValue = config.get(key).orElse(null); - } - if (configValue != null) { - break; - } - } - if (configValue == null && defaultAnnot != null) { - configValue = defaultAnnot.value(); - } - if (configValue != null) { - // net step perform expression resolution, if any - return Configuration.evaluateValue(configValue); - } - return null; - } /** - * This method reapplies a changed configuration value to the field. + * This method reapplies a changed configuration keys to the field. * * @param target the target instance, not null. - * @param configValue the new value to be applied, null will trigger the evaluation current the configured default value. - * @param resolve set to true, if expression resolution should be applied on the value passed. + * @param configValue the new keys to be applied, null will trigger the evaluation current the configured default keys. + * @param resolve set to true, if expression resolution should be applied on the keys passed. * @throws ConfigException if the configuration required could not be resolved or converted. */ - public void applyValue(Object target, String configValue, boolean resolve) throws ConfigException { + public void applyValue(Object target, String configValue, boolean resolve, Configuration... configurations) throws ConfigException { Objects.requireNonNull(target); try { if (resolve && configValue != null) { // net step perform exression resolution, if any - configValue = Configuration.evaluateValue(configValue); + configValue = Configuration.evaluateValue(configValue, configurations); } // Check for adapter/filter - WithPropertyAdapter adapterAnnot = this.annotatedField.getAnnotation(WithPropertyAdapter.class); - Class<? extends PropertyAdapter> propertyAdapterType; - if (adapterAnnot != null) { - propertyAdapterType = adapterAnnot.value(); - if (!propertyAdapterType.equals(PropertyAdapter.class)) { - // TODO cache here... - PropertyAdapter<String> filter = propertyAdapterType.newInstance(); - configValue = filter.adapt(configValue); - } - } - if (configValue == null) { - // TODO Check for optional injection! - // annotatedField.set(target, null); - LOG.info("No config found for " + - this.annotatedField.getDeclaringClass().getName() + '#' + - this.annotatedField.getName()); - } else { - Class baseType = annotatedField.getType(); - if (String.class.equals(baseType) || baseType.isAssignableFrom(configValue.getClass())) { - annotatedField.set(target, configValue); - } else { - PropertyAdapter<?> adapter = PropertyAdapter.getAdapter(baseType); - annotatedField.set(target, adapter.adapt(configValue)); - } - } + Object value = InjectionUtils.adaptValue(this.annotatedField, this.annotatedField.getType(), configValue); + annotatedField.setAccessible(true); + annotatedField.set(target, value); } catch (Exception e) { throw new ConfigException("Failed to annotation configured field: " + this.annotatedField.getDeclaringClass() .getName() + '.' + annotatedField.getName(), e); } } - /** - * Evaluates all absolute configuration key based on the annotations found on a class. - * - * @param areasAnnot the (optional) annotation definining areas to be looked up. - * @param propertyAnnotations the annotation on field/method level that may defined the - * exact key to be looked up (in absolute or relative form). - * @return the list current keys in order how they should be processed/looked up. - */ - private List<String> evaluateKeys(DefaultAreas areasAnnot,Collection<ConfiguredProperty> propertyAnnotations) { - Objects.requireNonNull(propertyAnnotations); - List<String> keys = propertyAnnotations.stream().map(ConfiguredProperty::value).filter(s -> !s.isEmpty()) - .collect(Collectors.toList()); - if (keys.isEmpty()) //noinspection UnusedAssignment - keys.add(annotatedField.getName()); - ListIterator<String> iterator = keys.listIterator(); - while (iterator.hasNext()) { - String next = iterator.next(); - if (next.startsWith("[") && next.endsWith("]")) { - // absolute key, strip away brackets, take key as is - iterator.set(next.substring(1, next.length() - 1)); - } else { - if (areasAnnot != null) { - // Remove original entry, since it will be replaced with prefixed entries - iterator.remove(); - // Add prefixed entries, including absolute (root) entry for "" area value. - for (String area : areasAnnot.value()) { - iterator.add(area.isEmpty() ? next : area + '.' + next); - } - } - } - } - return keys; - } /** * This method checks if the given (qualified) configuration key is referenced fromMap this field. @@ -191,26 +102,26 @@ public class ConfiguredField { * @param key the (qualified) configuration key, not null. * @return true, if the key is referenced. */ - public boolean matchesKey(String key) { - DefaultAreas areasAnnot = this.annotatedField.getDeclaringClass().getAnnotation(DefaultAreas.class); + public boolean matchesKey(String configName, String key) { Collection<ConfiguredProperty> configuredProperties = Utils.getAnnotations(this.annotatedField, ConfiguredProperty.class, ConfiguredProperties.class ); - List<String> keys = evaluateKeys(areasAnnot, configuredProperties); - return keys.contains(key); - } - - /** - * This method evaluates the {@link Configuration} that currently is valid for the given target field/method. - * - * @return the {@link Configuration} instance to be used, never null. - */ - public Configuration getConfiguration() { - WithConfig name = annotatedField.getAnnotation(WithConfig.class); - if(name!=null) { - return Configuration.current(name.value()); + for(ConfiguredProperty prop: configuredProperties){ + String currentName = prop.config().trim(); + if(currentName.isEmpty()){ + if(!"default".equals(configName)){ + continue; + } + } + else if(!currentName.equals(configName)){ + continue; + } + DefaultAreas areasAnnot = this.annotatedField.getDeclaringClass().getAnnotation(DefaultAreas.class); + List<String> keys = InjectionUtils.evaluateKeys(this.annotatedField, areasAnnot, prop); + if( keys.contains(key)){ + return true; + } } - return Configuration.current(); + return false; } - } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java deleted file mode 100644 index 2153777..0000000 --- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredInstancesManager.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.core.internal.inject; - -import org.apache.tamaya.Configuration; -import org.apache.tamaya.PropertySource; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.ref.WeakReference; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * This service class manages the configured instances that are currently attached to the configuration - * system. References to instances are rest as WeakReference instances, and cleanup current internal structures - * is performed implictly during event triggering for configuration changes. - * Created by Anatole on 03.10.2014. - */ -public final class ConfiguredInstancesManager implements PropertyChangeListener{ - - private static final ConfiguredInstancesManager INSTANCE = new ConfiguredInstancesManager(); - private Map<ConfiguredType,List<WeakReference<Object>>> configuredInstances = new ConcurrentHashMap<>(); - private final Object LOCK = new Object(); - - private ConfiguredInstancesManager(){ -// Configuration.addConfigChangeListener(this); - } - - public static <T> void register(ConfiguredType configuredType, Object instance) { - List<WeakReference<Object>> instances = INSTANCE.configuredInstances.get(configuredType); - if(instances==null){ - synchronized(INSTANCE.configuredInstances){ - instances = INSTANCE.configuredInstances.get(configuredType); - if(instances==null){ - instances = Collections.synchronizedList(new ArrayList<>()); - INSTANCE.configuredInstances.put(configuredType, instances); - } - } - } - synchronized(instances) { - instances.add(new WeakReference<>(instance)); - } - } - - @Override - public void propertyChange(PropertyChangeEvent propertyChangeEvent) { - for(Map.Entry<ConfiguredType,List<WeakReference<Object>>> en: configuredInstances.entrySet()){ - PropertySource propertyProvider = (PropertySource)propertyChangeEvent.getSource(); - if((propertyProvider instanceof Configuration) && en.getKey().isConfiguredBy((Configuration)propertyProvider)){ - List<WeakReference<Object>> instances = en.getValue(); - synchronized (instances){ - Iterator<WeakReference<Object>> iterator = instances.iterator(); - while (iterator.hasNext()) { - WeakReference<Object> ref = iterator.next(); - Object instance = ref.get(); - if(instance==null){ - iterator.remove(); - } - else{ - en.getKey().triggerConfigUpdate(propertyChangeEvent, instance); - } - } - } - } - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java index 3bc4472..3b83ee5 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredMethod.java @@ -18,9 +18,9 @@ */ package org.apache.tamaya.core.internal.inject; +import org.apache.tamaya.Codec; import org.apache.tamaya.ConfigException; import org.apache.tamaya.Configuration; -import org.apache.tamaya.PropertyAdapter; import org.apache.tamaya.annotation.*; import org.apache.tamaya.core.internal.Utils; @@ -31,8 +31,8 @@ import java.util.stream.Collectors; /** * Small class that contains and manages all information anc access to a configured field and a concrete instance current - * it (referenced by a weak reference). It also implements all aspects current value filtering, conversiong any applying the - * final value by reflection. + * it (referenced by a weak reference). It also implements all aspects current keys filtering, conversiong any applying the + * final keys by reflection. * Created by Anatole on 01.10.2014. */ @SuppressWarnings("UnusedDeclaration") @@ -55,71 +55,9 @@ public class ConfiguredMethod { } - /** - * Internally evaluated the current valid configuration value based on the given annotations present. - * - * @return the value to be returned, or null. - */ - private String getConfigValue() { - DefaultAreas areasAnnot = this.annotatedMethod.getDeclaringClass().getAnnotation(DefaultAreas.class); - DefaultValue defaultAnnot = this.annotatedMethod.getAnnotation(DefaultValue.class); - Collection<ConfiguredProperty> configuredProperties = - Utils.getAnnotations(this.annotatedMethod, ConfiguredProperty.class, ConfiguredProperties.class); - List<String> keys = evaluateKeys(areasAnnot, configuredProperties); - Configuration config = getConfiguration(); - String configValue = null; - for (String key : keys) { - if (config.containsKey(key)) { - configValue = config.get(key).orElse(null); - } - if (configValue != null) { - break; - } - } - if (configValue == null && defaultAnnot != null) { - configValue = defaultAnnot.value(); - } - if (configValue != null) { - // net step perform expression resolution, if any - return Configuration.evaluateValue(configValue); - } - return null; - } - /** - * Evaluates all absolute configuration key based on the annotations found on a class. - * - * @param areasAnnot the (optional) annotation definining areas to be looked up. - * @param propertyAnnotations the annotation on field/method level that may defined the - * exact key to be looked up (in absolute or relative form). - * @return the list current keys in order how they should be processed/looked up. - */ - private List<String> evaluateKeys(DefaultAreas areasAnnot, Collection<ConfiguredProperty> propertyAnnotations) { - List<String> keys = - Objects.requireNonNull(propertyAnnotations).stream() - .filter(p -> !p.value().isEmpty()) - .map(ConfiguredProperty::value).collect(Collectors.toList()); - if (keys.isEmpty()) //noinspection UnusedAssignment - keys.add(annotatedMethod.getName()); - ListIterator<String> iterator = keys.listIterator(); - while (iterator.hasNext()) { - String next = iterator.next(); - if (next.startsWith("[") && next.endsWith("]")) { - // absolute key, strip away brackets, take key as is - iterator.set(next.substring(1, next.length() - 1)); - } else { - if (areasAnnot != null) { - // Remove original entry, since it will be replaced with prefixed entries - iterator.remove(); - // Add prefixed entries, including absolute (root) entry for "" area value. - for (String area : areasAnnot.value()) { - iterator.add(area.isEmpty() ? next : area + '.' + next); - } - } - } - } - return keys; - } + + /** * This method checks if the given (qualified) configuration key is referenced fromMap this field. @@ -133,62 +71,23 @@ public class ConfiguredMethod { DefaultAreas areasAnnot = this.annotatedMethod.getDeclaringClass().getAnnotation(DefaultAreas.class); Collection<ConfiguredProperty> configuredProperties = Utils.getAnnotations(this.annotatedMethod, ConfiguredProperty.class, ConfiguredProperties.class); - List<String> keys = evaluateKeys(areasAnnot, configuredProperties); - return keys.contains(key); - } - - /** - * This method evaluates the {@link org.apache.tamaya.Configuration} that currently is valid for the given target field/method. - * - * @return the {@link org.apache.tamaya.Configuration} instance to be used, never null. - */ - public Configuration getConfiguration() { - WithConfig name = annotatedMethod.getAnnotation(WithConfig.class); - if (name != null) { - return Configuration.current(name.value()); - } - return Configuration.current(); - } - - /** - * This method reapplies a changed configuration value to the field. - * - * @throws org.apache.tamaya.ConfigException if the configuration required could not be resolved or converted. - */ - public Object getValue(Object[] args) throws ConfigException { - // TODO do something with additional args? - String configValue = getConfigValue(); - try { - // Check for adapter/filter - WithPropertyAdapter adapterAnnot = this.annotatedMethod.getAnnotation(WithPropertyAdapter.class); - Class<? extends PropertyAdapter> propertyAdapterType; - if (adapterAnnot != null) { - propertyAdapterType = adapterAnnot.value(); - if (!propertyAdapterType.equals(PropertyAdapter.class)) { - // TODO cache here... - PropertyAdapter<String> filter = propertyAdapterType.newInstance(); - configValue = filter.adapt(configValue); - } - } - if (configValue == null) { - // TODO optionally return null... - LOG.info("No config value found for " + - this.annotatedMethod.getDeclaringClass().getName() + '#' + - this.annotatedMethod.getName()); - return null; - } else { - Class<?> baseType = annotatedMethod.getReturnType(); - if (String.class.equals(baseType) || baseType.isAssignableFrom(configValue.getClass())) { - return configValue; - } else { - PropertyAdapter<?> adapter = PropertyAdapter.getAdapter(baseType); - return adapter.adapt(configValue); - } + for(ConfiguredProperty prop: configuredProperties) { + if (InjectionUtils.evaluateKeys(this.annotatedMethod, areasAnnot, prop).contains(key)) { + return true; } - } catch (Exception e) { - throw new ConfigException("Failed to annotation configured field: " + this.annotatedMethod.getDeclaringClass() - .getName() + '.' + annotatedMethod.getName(), e); } + return false; } + +// /** +// * This method reapplies a changed configuration keys to the field. +// * +// * @throws org.apache.tamaya.ConfigException if the configuration required could not be resolved or converted. +// */ +// public Object getValue(Object[] args, Configuration... configurations) throws ConfigException { +// // TODO do something with additional args? +// return InjectionUtils.adaptValue(this.annotatedMethod, this.annotatedMethod.getReturnType(), configValue); +// } + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredType.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredType.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredType.java index b199c23..ad89c3d 100644 --- a/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredType.java +++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/ConfiguredType.java @@ -20,6 +20,7 @@ package org.apache.tamaya.core.internal.inject; import org.apache.tamaya.ConfigException; import org.apache.tamaya.Configuration; +import org.apache.tamaya.PropertySource; import org.apache.tamaya.annotation.*; import java.beans.PropertyChangeEvent; @@ -32,41 +33,30 @@ import java.util.*; * Created by Anatole on 03.10.2014. */ public class ConfiguredType { - + /** A list with all annotated instance variables. */ private List<ConfiguredField> configuredFields = new ArrayList<>(); + /** A list with all annotated methods (templates). */ private Map<Method, ConfiguredMethod> configuredMethods = new HashMap<>(); + /** A list with all callback methods listening to config changes. */ private List<ConfigChangeCallbackMethod> callbackMethods = new ArrayList<>(); + /** The basic type. */ private Class type; + /** + * Creates an instance of this class hereby evaluating the config annotations given for later effective + * injection (configuration) of instances. + * @param type the instance type. + */ public ConfiguredType(Class type) { this.type = Objects.requireNonNull(type); - for (Field f : type.getDeclaredFields()) { - ConfiguredProperties propertiesAnnot = f.getAnnotation(ConfiguredProperties.class); - if (propertiesAnnot != null) { - try { - ConfiguredField configuredField = new ConfiguredField(f); - configuredFields.add(configuredField); - } catch (Exception e) { - throw new ConfigException("Failed to initialized configured field: " + - f.getDeclaringClass().getName() + '.' + f.getName(), e); - } - } - else{ - ConfiguredProperty propertyAnnot = f.getAnnotation(ConfiguredProperty.class); - if (propertyAnnot != null) { - try { - ConfiguredField configuredField = new ConfiguredField(f); - configuredFields.add(configuredField); - } catch (Exception e) { - throw new ConfigException("Failed to initialized configured field: " + - f.getDeclaringClass().getName() + '.' + f.getName(), e); - } - } - } - } + initFields(type); + initMethods(type); + } + + private void initMethods(Class type) { for (Method m : type.getDeclaredMethods()) { ObservesConfigChange mAnnot = m.getAnnotation(ObservesConfigChange.class); - if(mAnnot!=null) { + if (mAnnot != null) { if (m.getParameterTypes().length != 1) { continue; } @@ -82,8 +72,7 @@ public class ConfiguredType { throw new ConfigException("Failed to initialized configured callback method: " + m.getDeclaringClass().getName() + '.' + m.getName(), e); } - } - else{ + } else { ConfiguredProperties propertiesAnnot = m.getAnnotation(ConfiguredProperties.class); if (propertiesAnnot != null) { try { @@ -93,8 +82,7 @@ public class ConfiguredType { throw new ConfigException("Failed to initialized configured method: " + m.getDeclaringClass().getName() + '.' + m.getName(), e); } - } - else{ + } else { ConfiguredProperty propertyAnnot = m.getAnnotation(ConfiguredProperty.class); if (propertyAnnot != null) { try { @@ -110,23 +98,59 @@ public class ConfiguredType { } } - public Object getConfiguredValue(Method method, Object[] args) { - ConfiguredMethod m = this.configuredMethods.get(method); - return m.getValue(args); + private void initFields(Class type) { + for (Field f : type.getDeclaredFields()) { + ConfiguredProperties propertiesAnnot = f.getAnnotation(ConfiguredProperties.class); + if (propertiesAnnot != null) { + try { + ConfiguredField configuredField = new ConfiguredField(f); + configuredFields.add(configuredField); + } catch (Exception e) { + throw new ConfigException("Failed to initialized configured field: " + + f.getDeclaringClass().getName() + '.' + f.getName(), e); + } + } else { + ConfiguredProperty propertyAnnot = f.getAnnotation(ConfiguredProperty.class); + if (propertyAnnot != null) { + try { + ConfiguredField configuredField = new ConfiguredField(f); + configuredFields.add(configuredField); + } catch (Exception e) { + throw new ConfigException("Failed to initialized configured field: " + + f.getDeclaringClass().getName() + '.' + f.getName(), e); + } + } + } + } } - public void configure(Object instance) { + /** + * Method called to configure an instance. + * + * @param instance The instance to be configured. + * @param configurations Configuration instances that replace configuration served by services. This allows + * more easily testing and adaption. + */ + public void configure(Object instance, Configuration... configurations) { for (ConfiguredField field : configuredFields) { field.applyInitialValue(instance); } } - public void triggerConfigUpdate(PropertyChangeEvent configChangeEvent, Object instance) { + public void triggerConfigUpdate(PropertyChangeEvent evt, Object instance) { // TODO do check for right config ;) - configuredFields.stream().filter(field -> field.matchesKey(configChangeEvent.getPropertyName())).forEach(field -> field.applyValue(instance, (String) configChangeEvent.getNewValue(), false)); + configuredFields.stream().filter(field -> field.matchesKey(getName(evt.getSource()), evt.getPropertyName())).forEach(field -> field.applyValue(instance, (String) evt.getNewValue(), false)); for (ConfigChangeCallbackMethod callBack : this.callbackMethods) { - callBack.call(instance, configChangeEvent); + callBack.call(instance, evt); + } + } + + private String getName(Object source){ + if(source instanceof PropertySource){ + PropertySource ps = (PropertySource)source; + return ps.getMetaInfo().getName(); } + return "N/A"; } public boolean isConfiguredBy(Configuration configuration) { @@ -135,7 +159,7 @@ public class ConfiguredType { } public static boolean isConfigured(Class type) { - if(type.getAnnotation(DefaultAreas.class)!=null){ + if (type.getAnnotation(DefaultAreas.class) != null) { return true; } // if no class level annotation is there we might have field level annotations only http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/c9e62e24/core/src/main/java/org/apache/tamaya/core/internal/inject/InjectionUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/tamaya/core/internal/inject/InjectionUtils.java b/core/src/main/java/org/apache/tamaya/core/internal/inject/InjectionUtils.java new file mode 100644 index 0000000..3c073b6 --- /dev/null +++ b/core/src/main/java/org/apache/tamaya/core/internal/inject/InjectionUtils.java @@ -0,0 +1,170 @@ +package org.apache.tamaya.core.internal.inject; + +import org.apache.tamaya.Codec; +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.annotation.*; +import org.apache.tamaya.core.internal.Utils; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.*; + +/** + * Created by Anatole on 19.12.2014. + */ +final class InjectionUtils { + + private InjectionUtils(){} + + /** + * This method evaluates the {@link org.apache.tamaya.Configuration} that currently is valid for the given target field/method. + * + * @return the {@link org.apache.tamaya.Configuration} instance to be used, never null. + */ + public static Configuration getConfiguration(ConfiguredProperty prop, Configuration... configuration) { + String name = prop.config(); + if (name != null && !name.trim().isEmpty()) { + return Configuration.current(name.trim()); + } + return Configuration.current(); + } + + /** + * Evaluates all absolute configuration key based on the annotations found on a class. + * + * @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 list current keys in order how they should be processed/looked up. + */ + public static List<String> evaluateKeys(Member member, DefaultAreas areasAnnot, ConfiguredProperty propertyAnnotation) { + List<String> keys = new ArrayList<>(Arrays.asList(propertyAnnotation.keys())); + if (keys.isEmpty()) //noinspection UnusedAssignment + keys.add(member.getName()); + ListIterator<String> iterator = keys.listIterator(); + while (iterator.hasNext()) { + String next = iterator.next(); + if (next.startsWith("[") && next.endsWith("]")) { + // absolute key, strip away brackets, take key as is + iterator.set(next.substring(1, next.length() - 1)); + } else { + if (areasAnnot != null) { + // 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 : areasAnnot.value()) { + iterator.add(area.isEmpty() ? next : area + '.' + next); + } + } + } + } + return keys; + } + + /** + * Internally evaluated the current valid configuration keys based on the given annotations present. + * + * @return the keys to be returned, or null. + */ + public static String getConfigValue(Method method, Configuration... configurations) { + DefaultAreas areasAnnot = method.getDeclaringClass().getAnnotation(DefaultAreas.class); + WithLoadPolicy loadPolicy = Utils.getAnnotation(WithLoadPolicy.class, method, method.getDeclaringClass()); + return getConfigValueInternal(method, areasAnnot, loadPolicy, configurations); + } + + /** + * Internally evaluated the current valid configuration keys based on the given annotations present. + * + * @return the keys to be returned, or null. + */ + public static String getConfigValue(Field field, Configuration... configurations) { + DefaultAreas areasAnnot = field.getDeclaringClass().getAnnotation(DefaultAreas.class); + WithLoadPolicy loadPolicy = Utils.getAnnotation(WithLoadPolicy.class, field, field.getDeclaringClass()); + return getConfigValueInternal(field, areasAnnot, loadPolicy, configurations); + } + + /** + * Internally evaluated the current valid configuration keys based on the given annotations present. + * + * @return the keys to be returned, or null. + */ + private static String getConfigValueInternal(AnnotatedElement element, DefaultAreas areasAnnot, WithLoadPolicy loadPolicy, Configuration... configurations) { + Collection<ConfiguredProperty> configuredProperties = Utils.getAnnotations( + element, ConfiguredProperty.class, ConfiguredProperties.class); + DefaultValue defaultAnnot = element.getAnnotation(DefaultValue.class); + String configValue = null; + for(ConfiguredProperty prop: configuredProperties){ + List<String> keys = InjectionUtils.evaluateKeys((Member)element, areasAnnot, prop); + Configuration config = InjectionUtils.getConfiguration(prop, configurations); + for (String key : keys) { + if (config.containsKey(key)) { + configValue = config.get(key).orElse(null); + } + if (configValue != null) { + break; + } + } + if (configValue != null) { + // net step perform expression resolution, if any + return Configuration.evaluateValue(configValue, config); + } + } + if (configValue == null && defaultAnnot != null) { + return defaultAnnot.value(); + } + return null; + } + + public static <T> T adaptValue(AnnotatedElement element, Class<T> targetType, String configValue){ + try { + // Check for adapter/filter + T adaptedValue = null; + WithCodec codecAnnot = element.getAnnotation(WithCodec.class); + Class<? extends Codec> codecType; + if (codecAnnot != null) { + codecType = codecAnnot.value(); + if (!codecType.equals(Codec.class)) { + // TODO cache here... + Codec<String> codec = codecType.newInstance(); + adaptedValue = (T) codec.deserialize(configValue); + } + } + if (String.class.equals(targetType)) { + return (T)configValue; + } else { + Codec<?> adapter = Codec.getInstance(targetType); + return (T)adapter.deserialize(configValue); + } + } catch (Exception e) { + throw new ConfigException("Failed to annotate configured member: " + element, e); + } + } + + /** + * This method evaluates the {@link Configuration} that currently is valid for the given target field/method. + * @param configurations Configuration instances that replace configuration served by services. This allows + * more easily testing and adaption. + * @return the {@link Configuration} instance to be used, never null. + */ + public static Configuration getConfiguration(String name, Configuration... configurations) { + if(name!=null) { + for(Configuration conf: configurations){ + if(name.equals(conf.getMetaInfo().getName())){ + return conf; + } + } + return Configuration.current(name); + } + else{ + for(Configuration conf: configurations){ + if("default".equals(conf.getMetaInfo().getName())){ + return conf; + } + } + } + return Configuration.current(); + } +}