Repository: deltaspike Updated Branches: refs/heads/master a60cb32e8 -> 8e6fa365a
support dynamic converted config instances Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/6d0c4a27 Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/6d0c4a27 Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/6d0c4a27 Branch: refs/heads/master Commit: 6d0c4a272cbef0e56dd1c60ea4b3f4e4069b8eca Parents: a60cb32 Author: rmannibucau <[email protected]> Authored: Mon Nov 7 09:21:25 2016 +0100 Committer: rmannibucau <[email protected]> Committed: Mon Nov 7 09:21:25 2016 +0100 ---------------------------------------------------------------------- .../core/api/config/ConfigProperty.java | 7 + .../core/api/config/ConfigResolver.java | 21 ++- .../spi/config/BaseConfigPropertyProducer.java | 50 ++++--- .../impl/config/ConfigurationExtension.java | 134 +++++++++++++++++++ .../config/DefaultConfigPropertyProducer.java | 6 +- .../InjectableConfigPropertyTest.java | 12 ++ .../api/config/injectable/SettingsBean.java | 45 +++++++ .../META-INF/apache-deltaspike.properties | 4 +- 8 files changed, 256 insertions(+), 23 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigProperty.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigProperty.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigProperty.java index 260eaa2..a856ad7 100644 --- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigProperty.java +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigProperty.java @@ -144,4 +144,11 @@ public @interface ConfigProperty */ @Nonbinding boolean evaluateVariables() default true; + + /** + * Converter for this property. + * @return the converter to use to read this property in the expected type. + */ + @Nonbinding + Class<? extends ConfigResolver.Converter> converter() default ConfigResolver.Converter.class; } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java index f8f522b..effdd4a 100644 --- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/ConfigResolver.java @@ -18,6 +18,7 @@ */ package org.apache.deltaspike.core.api.config; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -760,6 +761,14 @@ public final class ConfigResolver <N> TypedResolver<N> as(Class<N> clazz); /** + * @param type target type, includes List and Map using a Converter + * @param converter The converter for the target type + * @param <N> target type + * @return this builder typed. + */ + <N> TypedResolver<N> as(Type type, Converter<N> converter); + + /** * Sets the type of the configuration entry to the given class, sets the converter to the one given and * returns this builder as a TypedResolver. If a converter is provided for one of the types supported by * default (see {@link #as(Class)} then the provided converter is used instead of the built-in one. @@ -794,7 +803,7 @@ public final class ConfigResolver private String keyResolved; - private Class<?> configEntryType = String.class; + private Type configEntryType = String.class; private boolean withDefault = false; private T defaultValue; @@ -845,6 +854,16 @@ public final class ConfigResolver } @Override + @SuppressWarnings("unchecked") + public <N> TypedResolver<N> as(Type clazz, Converter<N> converter) + { + configEntryType = clazz; + this.converter = converter; + + return (TypedResolver<N>) this; + } + + @Override public TypedResolver<T> withDefault(T value) { defaultValue = value; http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java index dec87f7..7cae68d 100644 --- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java @@ -21,9 +21,11 @@ package org.apache.deltaspike.core.spi.config; import javax.enterprise.inject.spi.InjectionPoint; import java.lang.annotation.Annotation; +import java.lang.reflect.Type; import org.apache.deltaspike.core.api.config.ConfigResolver; import org.apache.deltaspike.core.api.config.ConfigProperty; +import org.apache.deltaspike.core.api.provider.BeanProvider; import org.apache.deltaspike.core.util.BeanUtils; /** @@ -110,6 +112,11 @@ public abstract class BaseConfigPropertyProducer protected <T> T getPropertyValue(InjectionPoint injectionPoint, Class<T> ipCls) { + return getUntypedPropertyValue(injectionPoint, ipCls); + } + + protected <T> T getUntypedPropertyValue(InjectionPoint injectionPoint, Type ipCls) + { ConfigProperty configProperty = getAnnotation(injectionPoint, ConfigProperty.class); if (configProperty == null) @@ -117,25 +124,9 @@ public abstract class BaseConfigPropertyProducer throw new IllegalStateException("producer method called without @ConfigProperty being present!"); } - ConfigResolver.TypedResolver<T> resolver = ConfigResolver.resolve(configProperty.name()) - .as(ipCls) - .withCurrentProjectStage(configProperty.projectStageAware()); - - String stringDefault = configProperty.defaultValue(); - if (!ConfigProperty.NULL.equals(stringDefault)) - { - resolver.withStringDefault(stringDefault); - } - - String parameterizedBy = configProperty.parameterizedBy(); - if (!ConfigProperty.NULL.equals(parameterizedBy)) - { - resolver.parameterizedBy(parameterizedBy); - } - - resolver.evaluateVariables(configProperty.evaluateVariables()); - - return resolver.getValue(); + return readEntry(configProperty.name(), configProperty.defaultValue(), ipCls, + configProperty.converter(), configProperty.parameterizedBy(), + configProperty.projectStageAware(), configProperty.evaluateVariables()); } /** @@ -169,4 +160,25 @@ public abstract class BaseConfigPropertyProducer { return BeanUtils.extractAnnotation(injectionPoint.getAnnotated(), targetType); } + + public <T> T readEntry(final String key, final String stringDefault, final Type ipCls, + final Class<? extends ConfigResolver.Converter> converterType, + final String parameterizedBy, final boolean projectStageAware, final boolean evaluate) + { + final ConfigResolver.UntypedResolver<String> untypedResolver = ConfigResolver.resolve(key); + final ConfigResolver.TypedResolver<T> resolver = + (ConfigResolver.Converter.class == converterType ? + untypedResolver.as(Class.class.cast(ipCls)) : + untypedResolver.as(ipCls, BeanProvider.getContextualReference(converterType))) + .withCurrentProjectStage(projectStageAware); + if (!ConfigProperty.NULL.equals(stringDefault)) + { + resolver.withStringDefault(stringDefault); + } + if (!ConfigProperty.NULL.equals(parameterizedBy)) + { + resolver.parameterizedBy(parameterizedBy); + } + return resolver.evaluateVariables(evaluate).getValue(); + } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java index 09ac956..accabae 100644 --- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java @@ -18,13 +18,24 @@ */ package org.apache.deltaspike.core.impl.config; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.Typed; +import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.AfterDeploymentValidation; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.BeforeBeanDiscovery; import javax.enterprise.inject.spi.BeforeShutdown; import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.spi.ProcessBean; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -32,10 +43,12 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.apache.deltaspike.core.api.config.ConfigProperty; import org.apache.deltaspike.core.api.config.ConfigResolver; import org.apache.deltaspike.core.api.config.PropertyFileConfig; import org.apache.deltaspike.core.api.exclude.Exclude; import org.apache.deltaspike.core.spi.activation.Deactivatable; +import org.apache.deltaspike.core.spi.config.BaseConfigPropertyProducer; import org.apache.deltaspike.core.spi.config.ConfigSource; import org.apache.deltaspike.core.spi.config.ConfigValidator; import org.apache.deltaspike.core.util.ClassDeactivationUtils; @@ -69,6 +82,9 @@ public class ConfigurationExtension implements Extension, Deactivatable private List<Class<? extends PropertyFileConfig>> propertyFileConfigClasses = new ArrayList<Class<? extends PropertyFileConfig>>(); + private final Set<Type> dynamicConfigTypes = new HashSet<Type>(); + private Bean<DynamicBeanProducer> dynamicProducer; + @SuppressWarnings("UnusedDeclaration") protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery) { @@ -104,6 +120,33 @@ public class ConfigurationExtension implements Extension, Deactivatable propertyFileConfigClasses.add(pcsClass); } + public void findDynamicProducer(@Observes ProcessBean<DynamicBeanProducer> processBean) + { + dynamicProducer = processBean.getBean(); + } + + public void collectDynamicTypes(@Observes ProcessBean<?> processBean) + { + for (final InjectionPoint ip : processBean.getBean().getInjectionPoints()) + { + final ConfigProperty annotation = ip.getAnnotated().getAnnotation(ConfigProperty.class); + if (annotation == null || annotation.converter() == ConfigResolver.Converter.class) + { + continue; + } + + dynamicConfigTypes.add(ip.getType()); + } + } + + public void addDynamicBean(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager bm) + { + if (dynamicProducer != null && !dynamicConfigTypes.isEmpty()) + { + afterBeanDiscovery.addBean(new DynamicBean(dynamicProducer, dynamicConfigTypes)); + } + } + @SuppressWarnings("UnusedDeclaration") public void registerUserConfigSources(@Observes AfterDeploymentValidation adv) { @@ -229,4 +272,95 @@ public class ConfigurationExtension implements Extension, Deactivatable } } } + + @ApplicationScoped + @Typed(DynamicBeanProducer.class) // used as an internal bean + static class DynamicBeanProducer extends BaseConfigPropertyProducer + { + @Produces + @ConfigProperty(name = "ignored") + public Object create(final InjectionPoint ip) + { + return super.getUntypedPropertyValue(ip, ip.getType()); + } + } + + @Typed + private static final class DynamicBean<T> implements Bean<T> + { + private final Bean<T> producer; + private final Set<Type> types; + + private DynamicBean(final Bean<T> producer, final Set<Type> types) + { + this.producer = producer; + this.types = types; + } + + @Override + public Set<Type> getTypes() + { + return types; + } + + @Override + public Set<Annotation> getQualifiers() + { + return producer.getQualifiers(); + } + + @Override + public Class<? extends Annotation> getScope() + { + return producer.getScope(); + } + + @Override + public String getName() + { + return producer.getName(); + } + + @Override + public boolean isNullable() + { + return producer.isNullable(); + } + + @Override + public Set<InjectionPoint> getInjectionPoints() + { + return producer.getInjectionPoints(); + } + + @Override + public Class<?> getBeanClass() + { + return producer.getBeanClass(); + } + + @Override + public Set<Class<? extends Annotation>> getStereotypes() + { + return producer.getStereotypes(); + } + + @Override + public boolean isAlternative() + { + return producer.isAlternative(); + } + + @Override + public T create(final CreationalContext<T> creationalContext) + { + return producer.create(creationalContext); + } + + @Override + public void destroy(final T t, final CreationalContext<T> creationalContext) + { + producer.destroy(t, creationalContext); + } + } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/DefaultConfigPropertyProducer.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/DefaultConfigPropertyProducer.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/DefaultConfigPropertyProducer.java index 8546c71..7e7efeb 100644 --- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/DefaultConfigPropertyProducer.java +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/DefaultConfigPropertyProducer.java @@ -26,6 +26,8 @@ import javax.enterprise.inject.spi.InjectionPoint; import org.apache.deltaspike.core.spi.config.BaseConfigPropertyProducer; import org.apache.deltaspike.core.api.config.ConfigProperty; +import java.lang.reflect.Type; + /** * This class contains producer methods for injecting * configuration provided with the {@link ConfigProperty} @@ -93,11 +95,11 @@ public class DefaultConfigPropertyProducer extends BaseConfigPropertyProducer } - private <T> T getPropertyWithException(InjectionPoint ip, Class<T> ipCls) + private <T> T getPropertyWithException(InjectionPoint ip, Type ipCls) { try { - return getPropertyValue(ip, ipCls); + return getUntypedPropertyValue(ip, ipCls); } catch (RuntimeException rte) { http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java index 568d26e..d6f11c2 100644 --- a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java +++ b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java @@ -18,6 +18,7 @@ */ package org.apache.deltaspike.test.core.api.config.injectable; +import java.net.MalformedURLException; import java.net.URL; import org.apache.deltaspike.core.api.provider.BeanProvider; @@ -37,6 +38,10 @@ import org.junit.runner.RunWith; import org.apache.deltaspike.test.core.api.config.injectable.numberconfig.NumberConfiguredBean; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertEquals; + @RunWith(Arquillian.class) @Category(SeCategory.class) //X TODO this is only SeCategory as there is currently an Arq problem with properties! @@ -104,4 +109,11 @@ public class InjectableConfigPropertyTest Assert.assertEquals(Float.valueOf(123.45f), numberBean.getPropertyFromConfig()); Assert.assertEquals(Float.valueOf(42.42f), numberBean.getPropertyNonexistingDefaulted()); } + + @Test + public void checkDynamicConvertedInjections() throws MalformedURLException { + SettingsBean settingsBean = BeanProvider.getContextualReference(SettingsBean.class, false); + assertEquals(asList(new URL("http://localhost"), new URL("http://127.0.0.1")), settingsBean.getUrlList()); + assertEquals(singletonList(new URL("http://127.0.0.2")), settingsBean.getUrlListFromProperties()); + } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/SettingsBean.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/SettingsBean.java b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/SettingsBean.java index d180aca..b542e77 100644 --- a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/SettingsBean.java +++ b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/SettingsBean.java @@ -19,9 +19,14 @@ package org.apache.deltaspike.test.core.api.config.injectable; import org.apache.deltaspike.core.api.config.ConfigProperty; +import org.apache.deltaspike.core.api.config.ConfigResolver; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; @ApplicationScoped public class SettingsBean @@ -92,6 +97,14 @@ public class SettingsBean @ConfigProperty(name = "testDbConfig") private String dbConfig; + @Inject + @ConfigProperty(name = "urlList", converter = UrlList.class, defaultValue = "http://localhost,http://127.0.0.1") + private List<URL> urlList; + + @Inject + @ConfigProperty(name = "urlListFromProperties", converter = UrlList.class) + private List<URL> urlListFromProperties; + protected SettingsBean() { } @@ -193,4 +206,36 @@ public class SettingsBean { return dbConfig; } + + public List<URL> getUrlList() { + return urlList; + } + + public List<URL> getUrlListFromProperties() { + return urlListFromProperties; + } + + public static class UrlList implements ConfigResolver.Converter<List<URL>> + { + @Override + public List<URL> convert(final String value) + { + final List<URL> urls = new ArrayList<URL>(); + if (value != null) + { + for (final String segment : value.split(",")) + { + try + { + urls.add(new URL(segment)); + } + catch (final MalformedURLException e) + { + throw new IllegalArgumentException(e); + } + } + } + return urls; + } + } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d0c4a27/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties b/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties index d8fee1c..61a86bf 100644 --- a/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties +++ b/deltaspike/core/impl/src/test/resources/META-INF/apache-deltaspike.properties @@ -47,4 +47,6 @@ propertyFloat=123.45 my.very.secret=onlyIDoKnowIt -deactivate.org.apache.deltaspike.test.core.impl.activation.DeactivatedClass=true \ No newline at end of file +deactivate.org.apache.deltaspike.test.core.impl.activation.DeactivatedClass=true + +urlListFromProperties = http://127.0.0.2
