I'll take a look in the evening. LieGrue, Strub
> Am 09.08.2017 um 14:00 schrieb John D. Ament <[email protected]>: > > Ok, I got tests passing on all three. It actually looks like its simpler > (and more correct) to not use a custom provider. The provider approach > wasn't supporting default values. I've committed changes, please take a look > and hopefully we can plan to reroll 1.0. > > John > >> On Tue, Aug 8, 2017 at 10:02 PM John D. Ament <[email protected]> wrote: >> Ok, I've readded some of the Weld3 support, 3 tests are failing. >> >> Some interesting notes: >> >> - If I remove Provider support, it generally works. However, I suddenly get >> duplicate beans of type Provider, which is confusing (but probably because >> of the alternative flag). >> - I can get everything to pass in Weld, and 1 test failure in in OWB with >> the following: >> >> - Set alternative = false >> - Remove Provider from the types >> - Remove the filter on types to check if class. >> - Change getId to use all of the types (makes a better toString) >> >> >> If we can figure out the alternative issue with OWB, we'll be good to go >> again. >> >> John >> >>> On Tue, Aug 8, 2017 at 8:44 AM John D. Ament <[email protected]> wrote: >>> I can get to some of it. >>> >>> We have a couple of issues now though: >>> >>> https://issues.jboss.org/browse/WELD-2411 >>> https://issues.jboss.org/browse/CDI-712 >>> >>> Martin doesn't feel the spec allows you to override Provider impl's. >>> Please feel free to weigh in :-) >>> >>> John >>> >>> >>>> On Tue, Aug 8, 2017 at 7:33 AM Mark Struberg <[email protected]> wrote: >>>> Oh I see what you mean. >>>> Do you mind to re-apply your fix or should I? >>>> >>>> Btw, I also tested with the -PWeld3 but I was not able to resolve >>>> arquillian-weld-embedded:jar:2.0.0-SNAPSHOT >>>> We might need to add a <repository> for it in the Weld3 profile. >>>> >>>> I also would love to add a Weld profile. I thought we have this already >>>> but seems we missed it? >>>> >>>> txs and LieGrue, >>>> strub >>>> >>>> > Am 08.08.2017 um 12:58 schrieb John D. Ament <[email protected]>: >>>> > >>>> > One note - hardcoding "isAlternative" to true breaks in Weld. Unless >>>> > there is a base bean of same type, the alternative is ignored. Hence >>>> > why I only did isAlternative on the provider type, all others are just >>>> > regular beans. >>>> > >>>> > John >>>> > >>>> > On Tue, Aug 8, 2017 at 6:55 AM <[email protected]> wrote: >>>> > Author: struberg >>>> > Date: Tue Aug 8 10:55:56 2017 >>>> > New Revision: 1804397 >>>> > >>>> > URL: http://svn.apache.org/viewvc?rev=1804397&view=rev >>>> > Log: >>>> > GERONIMO-6577 move back to a more dynamic version >>>> > >>>> > The goal of r1800748 to calculate all information upfront could not be >>>> > achieved >>>> > so we move back to the old version >>>> > >>>> > Modified: >>>> > geronimo/components/config/trunk/impl/debug-suite.xml >>>> > geronimo/components/config/trunk/impl/pom.xml >>>> > >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigExtension.java >>>> > >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigInjectionBean.java >>>> > >>>> > geronimo/components/config/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProviderTest.java >>>> > geronimo/components/config/trunk/pom.xml >>>> > >>>> > Modified: geronimo/components/config/trunk/impl/debug-suite.xml >>>> > URL: >>>> > http://svn.apache.org/viewvc/geronimo/components/config/trunk/impl/debug-suite.xml?rev=1804397&r1=1804396&r2=1804397&view=diff >>>> > ============================================================================== >>>> > --- geronimo/components/config/trunk/impl/debug-suite.xml (original) >>>> > +++ geronimo/components/config/trunk/impl/debug-suite.xml Tue Aug 8 >>>> > 10:55:56 2017 >>>> > @@ -24,7 +24,7 @@ >>>> > <classes> >>>> > <!-- Issues in the spec --> >>>> > <!-- CDI-437 --> >>>> > - <class >>>> > name="org.eclipse.microprofile.config.tck.CdiOptionalInjectionTest"> >>>> > + <class >>>> > name="org.eclipse.microprofile.config.tck.CDIPlainInjectionTest"> >>>> > <methods> >>>> > <include name=".*"/> >>>> > </methods> >>>> > >>>> > Modified: geronimo/components/config/trunk/impl/pom.xml >>>> > URL: >>>> > http://svn.apache.org/viewvc/geronimo/components/config/trunk/impl/pom.xml?rev=1804397&r1=1804396&r2=1804397&view=diff >>>> > ============================================================================== >>>> > --- geronimo/components/config/trunk/impl/pom.xml (original) >>>> > +++ geronimo/components/config/trunk/impl/pom.xml Tue Aug 8 10:55:56 >>>> > 2017 >>>> > @@ -27,7 +27,6 @@ >>>> > </parent> >>>> > >>>> > <artifactId>geronimo-config-impl</artifactId> >>>> > - <name>Geronimo Microprofile Configuration :: Implementation</name> >>>> > >>>> > <dependencyManagement> >>>> > <dependencies> >>>> > >>>> > Modified: >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigExtension.java >>>> > URL: >>>> > http://svn.apache.org/viewvc/geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigExtension.java?rev=1804397&r1=1804396&r2=1804397&view=diff >>>> > ============================================================================== >>>> > --- >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigExtension.java >>>> > (original) >>>> > +++ >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigExtension.java >>>> > Tue Aug 8 10:55:56 2017 >>>> > @@ -16,54 +16,38 @@ >>>> > */ >>>> > package org.apache.geronimo.config.cdi; >>>> > >>>> > -import org.apache.geronimo.config.ConfigImpl; >>>> > -import org.eclipse.microprofile.config.Config; >>>> > -import org.eclipse.microprofile.config.inject.ConfigProperty; >>>> > -import org.eclipse.microprofile.config.spi.ConfigProviderResolver; >>>> > +import java.lang.reflect.Type; >>>> > +import java.util.ArrayList; >>>> > +import java.util.HashMap; >>>> > +import java.util.HashSet; >>>> > +import java.util.List; >>>> > +import java.util.Map; >>>> > +import java.util.Optional; >>>> > +import java.util.Set; >>>> > +import java.util.stream.Collectors; >>>> > >>>> > -import javax.enterprise.context.spi.CreationalContext; >>>> > import javax.enterprise.event.Observes; >>>> > import javax.enterprise.inject.spi.AfterBeanDiscovery; >>>> > import javax.enterprise.inject.spi.AfterDeploymentValidation; >>>> > -import javax.enterprise.inject.spi.AnnotatedMember; >>>> > -import javax.enterprise.inject.spi.AnnotatedType; >>>> > import javax.enterprise.inject.spi.BeanManager; >>>> > -import javax.enterprise.inject.spi.BeforeBeanDiscovery; >>>> > import javax.enterprise.inject.spi.BeforeShutdown; >>>> > +import javax.enterprise.inject.spi.DeploymentException; >>>> > import javax.enterprise.inject.spi.Extension; >>>> > import javax.enterprise.inject.spi.InjectionPoint; >>>> > import javax.enterprise.inject.spi.ProcessInjectionPoint; >>>> > import javax.inject.Provider; >>>> > -import java.lang.reflect.ParameterizedType; >>>> > -import java.lang.reflect.Type; >>>> > -import java.util.ArrayList; >>>> > -import java.util.Arrays; >>>> > -import java.util.Collection; >>>> > -import java.util.HashMap; >>>> > -import java.util.HashSet; >>>> > -import java.util.List; >>>> > -import java.util.Map; >>>> > -import java.util.Objects; >>>> > -import java.util.Optional; >>>> > -import java.util.Set; >>>> > -import java.util.function.BiFunction; >>>> > -import java.util.stream.Stream; >>>> > >>>> > -import static java.util.function.Function.identity; >>>> > -import static java.util.stream.Collectors.toMap; >>>> > -import static org.eclipse.microprofile.config.ConfigProvider.getConfig; >>>> > +import org.apache.geronimo.config.DefaultConfigProvider; >>>> > +import org.eclipse.microprofile.config.Config; >>>> > +import org.eclipse.microprofile.config.ConfigProvider; >>>> > +import org.eclipse.microprofile.config.inject.ConfigProperty; >>>> > >>>> > /** >>>> > * @author <a href="mailto:[email protected]">Mark Struberg</a> >>>> > */ >>>> > public class ConfigExtension implements Extension { >>>> > - private static final Object NULL = new Object(); >>>> > - >>>> > private Config config; >>>> > - private ConfigProviderResolver resolver; >>>> > >>>> > - private Set<Injection> injections = new HashSet<>(); >>>> > - private List<Throwable> deploymentProblems = new ArrayList<>(); >>>> > private static final Map<Type, Type> REPLACED_TYPES = new >>>> > HashMap<>(); >>>> > >>>> > static { >>>> > @@ -74,336 +58,65 @@ public class ConfigExtension implements >>>> > REPLACED_TYPES.put(boolean.class, Boolean.class); >>>> > } >>>> > >>>> > - void init(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, >>>> > final BeanManager bm) { >>>> > - resolver = ConfigProviderResolver.instance(); >>>> > - config = getConfig(); >>>> > - } >>>> > + private Set<InjectionPoint> injectionPoints = new HashSet<>(); >>>> > + >>>> > >>>> > public void collectConfigProducer(@Observes >>>> > ProcessInjectionPoint<?, ?> pip) { >>>> > - final InjectionPoint injectionPoint = pip.getInjectionPoint(); >>>> > - final ConfigProperty configProperty = >>>> > injectionPoint.getAnnotated().getAnnotation(ConfigProperty.class); >>>> > + ConfigProperty configProperty = >>>> > pip.getInjectionPoint().getAnnotated().getAnnotation(ConfigProperty.class); >>>> > if (configProperty != null) { >>>> > - Type replacedType = >>>> > REPLACED_TYPES.getOrDefault(injectionPoint.getType(), >>>> > injectionPoint.getType()); >>>> > - Injection injection = new Injection(replacedType); >>>> > - final String key = getConfigKey(injectionPoint, >>>> > configProperty); >>>> > - final boolean defaultUnset = >>>> > isDefaultUnset(configProperty.defaultValue()); >>>> > - if (!injections.add(injection)) { >>>> > - final Injection ref = injection; >>>> > - injection = injections.stream().filter(i -> >>>> > i.equals(ref)).findFirst().get(); >>>> > - } >>>> > - injection.keys.add(key); >>>> > - injection.defaultValues.add(configProperty.defaultValue()); >>>> > - >>>> > - final ConfigImpl configImpl = unwrapConfig(); >>>> > - >>>> > - // what about lazy runtime lookup, not consistent with tck >>>> > and system prop usage, for now assume optional=optional ;) >>>> > - boolean hasValue = true; >>>> > - if (defaultUnset) { // value validation >>>> > - if (ParameterizedType.class.isInstance(injection.type)) >>>> > { >>>> > - final ParameterizedType pt = >>>> > ParameterizedType.class.cast(injection.type); >>>> > - if (pt.getRawType() != Optional.class && >>>> > !configImpl.getOptionalValue(key, String.class).isPresent()) { >>>> > - hasValue = false; >>>> > - } >>>> > - } else if (!configImpl.getOptionalValue(key, >>>> > String.class).isPresent()) { >>>> > - hasValue = false; >>>> > - } >>>> > - if (!hasValue) { >>>> > - deploymentProblems.add(new >>>> > IllegalArgumentException("No configured value for '" + key + "' from " + >>>> > injectionPoint)); >>>> > - } >>>> > - } >>>> > - >>>> > - Class<?> instanceType = null; >>>> > - if (ParameterizedType.class.isInstance(injection.type)) { >>>> > // converters validation >>>> > - final ParameterizedType pt = >>>> > ParameterizedType.class.cast(injection.type); >>>> > - if (pt.getRawType() == Provider.class && >>>> > pt.getActualTypeArguments().length == 1 && >>>> > Class.class.isInstance(pt.getActualTypeArguments()[0]) >>>> > - && >>>> > !configImpl.getConverters().containsKey(Class.class.cast(pt.getActualTypeArguments()[0]))) >>>> > { >>>> > - instanceType = >>>> > Class.class.cast(pt.getActualTypeArguments()[0]); >>>> > - } // else if Optional it is fine, else we don't know >>>> > how to process >>>> > - } else if (Class.class.isInstance(injection.type)) { >>>> > - instanceType = Class.class.cast(injection.type); >>>> > - } >>>> > - if (instanceType != null) { // validate we have a converter >>>> > + we can convert the existing value >>>> > - if >>>> > (!configImpl.getConverters().containsKey(instanceType)) { >>>> > - deploymentProblems.add(new >>>> > IllegalArgumentException("Missing converter for '" + key + "' from " + >>>> > injectionPoint)); >>>> > - } else if (hasValue) { >>>> > - try { >>>> > - >>>> > configImpl.getConverters().get(injection.type).convert(configImpl.getValue(key)); >>>> > - } catch (final RuntimeException re) { >>>> > - deploymentProblems.add(re); >>>> > - } >>>> > - } >>>> > - } >>>> > + injectionPoints.add(pip.getInjectionPoint()); >>>> > } >>>> > } >>>> > >>>> > public void registerConfigProducer(@Observes AfterBeanDiscovery >>>> > abd, BeanManager bm) { >>>> > - injections.stream() >>>> > - .flatMap(injection -> { >>>> > - final BiFunction<CreationalContext<?>, >>>> > ConfigInjectionBean<?>, String> keyProvider; >>>> > - if (injection.keys.size() == 1) { >>>> > - final String key = >>>> > injection.keys.iterator().next(); >>>> > - keyProvider = (ctx, bean) -> key; >>>> > - } else { >>>> > - keyProvider = (ctx, bean) -> >>>> > getName(findInjectionPoint(bm, ctx, bean)); >>>> > - } >>>> > - >>>> > - if >>>> > (ParameterizedType.class.isInstance(injection.type)) { >>>> > - final ParameterizedType paramType = >>>> > ParameterizedType.class.cast(injection.type); >>>> > - final Type rawType = paramType.getRawType(); >>>> > - >>>> > - // todo: do we care of Instance injection? >>>> > doesnt make much sense right? >>>> > - if (Provider.class == rawType && >>>> > paramType.getActualTypeArguments().length == 1) { >>>> > - if >>>> > (!Class.class.isInstance(paramType.getActualTypeArguments()[0])) { >>>> > - deploymentProblems.add(new >>>> > IllegalArgumentException("@ConfigProperty can only be used with >>>> > Provider<T> where T is a Class")); >>>> > - return Stream.empty(); >>>> > - } >>>> > - final Class<?> providerType = >>>> > Class.class.cast(paramType.getActualTypeArguments()[0]); >>>> > - return Stream.of(new >>>> > ConfigInjectionBean<Provider<?>>(injection.type, true) { >>>> > - @Override >>>> > - public Provider<?> create(final >>>> > CreationalContext<Provider<?>> context) { >>>> > - return () -> >>>> > config.getValue(keyProvider.apply(context, this), providerType); >>>> > - } >>>> > - }); >>>> > - } else if (Optional.class == rawType && >>>> > paramType.getActualTypeArguments().length == 1) { >>>> > - if >>>> > (!Class.class.isInstance(paramType.getActualTypeArguments()[0])) { >>>> > - deploymentProblems.add(new >>>> > IllegalArgumentException("@ConfigProperty can only be used with >>>> > Optional<T> where T is a Class")); >>>> > - return null; >>>> > - } >>>> > - final Class<?> optionalType = >>>> > Class.class.cast(paramType.getActualTypeArguments()[0]); >>>> > - return Stream.of(new >>>> > ConfigInjectionBean<Optional<?>>(injection.type) { >>>> > - @Override >>>> > - public Optional<?> create(final >>>> > CreationalContext<Optional<?>> context) { >>>> > - return >>>> > config.getOptionalValue(keyProvider.apply(context, this), optionalType); >>>> > - } >>>> > - }); >>>> > - } else { >>>> > - deploymentProblems.add(new >>>> > IllegalArgumentException("Unsupported parameterized type " + paramType)); >>>> > - return Stream.empty(); >>>> > - } >>>> > - } else if (Class.class.isInstance(injection.type)) { >>>> > - final Class clazz = >>>> > Class.class.cast(injection.type); >>>> > - final ConfigInjectionBean bean; >>>> > - if (injection.defaultValues.isEmpty()) { >>>> > - bean = new >>>> > ConfigInjectionBean<Object>(injection.type) { >>>> > - @Override >>>> > - public Object create(final >>>> > CreationalContext<Object> context) { >>>> > - return >>>> > config.getOptionalValue(keyProvider.apply(context, this), clazz); >>>> > - } >>>> > - }; >>>> > - } else if (injection.defaultValues.size() == 1) >>>> > { // common enough to be optimized >>>> > - final String defVal = >>>> > injection.defaultValues.iterator().next(); >>>> > - final Object alternativeVal = >>>> > isDefaultUnset(defVal) ? null : unwrapConfig().convert(defVal, clazz); >>>> > - bean = new >>>> > ConfigInjectionBean<Object>(injection.type) { >>>> > - @Override >>>> > - public Object create(final >>>> > CreationalContext<Object> context) { >>>> > - final Optional optionalValue = >>>> > config.getOptionalValue(keyProvider.apply(context, this), clazz); >>>> > - return >>>> > optionalValue.orElse(alternativeVal); >>>> > - } >>>> > - }; >>>> > - } else { // sadly we need to get back to the >>>> > injection point to know which one we need to use >>>> > - final Map<String, Object> prepared = >>>> > injection.defaultValues.stream() >>>> > - .collect(toMap(identity(), k -> >>>> > isDefaultUnset(k) ? NULL : unwrapConfig().convert(k, clazz), (a, b) -> >>>> > b)); >>>> > - bean = new >>>> > ConfigInjectionBean<Object>(injection.type) { >>>> > - @Override >>>> > - public Object create(final >>>> > CreationalContext<Object> context) { >>>> > - final InjectionPoint ip = >>>> > findInjectionPoint(bm, context, this); >>>> > - if (ip == null) { >>>> > - throw new >>>> > IllegalStateException("Could not retrieve InjectionPoint"); >>>> > - } >>>> > - return >>>> > config.getOptionalValue(ConfigExtension.this.getName(ip), clazz) >>>> > - .orElseGet(() -> { >>>> > - final Object val = >>>> > prepared.get(ip.getAnnotated().getAnnotation(ConfigProperty.class).defaultValue()); >>>> > - return val == NULL ? >>>> > null : val; >>>> > - }); >>>> > - } >>>> > - }; >>>> > - } >>>> > - >>>> > - final Collection<ConfigInjectionBean<?>> beans >>>> > = new ArrayList<>(); >>>> > - beans.add(bean); >>>> > - >>>> > - // is adding these beans is that useful? we >>>> > captured them all so only a programmatic lookup would justify it >>>> > - // and not sure it would be done this way anyway >>>> > - final ParameterizedTypeImpl providerType = new >>>> > ParameterizedTypeImpl(Provider.class, injection.type); >>>> > - if (injections.stream().noneMatch(i -> >>>> > i.type.equals(providerType))) { >>>> > - beans.add(new >>>> > ConfigInjectionBean<Provider<?>>(providerType, true) { >>>> > - @Override >>>> > - public Provider<?> create(final >>>> > CreationalContext<Provider<?>> context) { >>>> > - return () -> bean.create(context); >>>> > - } >>>> > - }); >>>> > - } >>>> > - >>>> > - final ParameterizedTypeImpl optionalType = new >>>> > ParameterizedTypeImpl(Optional.class, injection.type); >>>> > - if (injections.stream().noneMatch(i -> >>>> > i.type.equals(optionalType))) { >>>> > - beans.add(new >>>> > ConfigInjectionBean<Optional<?>>(optionalType) { >>>> > - @Override >>>> > - public Optional<?> create(final >>>> > CreationalContext<Optional<?>> context) { >>>> > - return >>>> > Optional.ofNullable(bean.create(context)); >>>> > - } >>>> > - }); >>>> > - } >>>> > - >>>> > - return beans.stream(); >>>> > - } else { >>>> > - deploymentProblems.add(new >>>> > IllegalArgumentException("Unknown type " + injection.type)); >>>> > - return Stream.empty(); >>>> > - } >>>> > - }) >>>> > - .forEach(abd::addBean); >>>> > - } >>>> > + Set<Class> types = injectionPoints.stream() >>>> > + .filter(ip -> ip.getType() instanceof Class) >>>> > + .map(ip -> (Class) >>>> > REPLACED_TYPES.getOrDefault(ip.getType(), ip.getType())) >>>> > + .collect(Collectors.toSet()); >>>> > + >>>> > + // Provider and Optional are ParameterizedTypes and not a >>>> > Class, so we need to add them manually >>>> > + types.add(Provider.class); >>>> > + types.add(Optional.class); >>>> > >>>> > - public void validate(@Observes AfterDeploymentValidation add) { >>>> > - deploymentProblems.forEach(add::addDeploymentProblem); >>>> > - injections.clear(); >>>> > - deploymentProblems.clear(); >>>> > + types.forEach(type -> abd.addBean(new ConfigInjectionBean(bm, >>>> > type))); >>>> > } >>>> > >>>> > - public void shutdown(@Observes BeforeShutdown bsd) { >>>> > - resolver.releaseConfig(config); >>>> > - } >>>> > + public void validate(@Observes AfterDeploymentValidation add) { >>>> > + List<String> deploymentProblems = new ArrayList<>(); >>>> > >>>> > - private ConfigImpl unwrapConfig() { >>>> > - return ConfigImpl.class.cast(config); >>>> > - } >>>> > + config = ConfigProvider.getConfig(); >>>> > >>>> > - private static String getName(final InjectionPoint ip) { >>>> > - final ConfigProperty annotation = >>>> > ip.getAnnotated().getAnnotation(ConfigProperty.class); >>>> > - final String name = annotation.name(); >>>> > - return isDefaultUnset(name) ? getConfigKey(ip, annotation) : >>>> > name; >>>> > - } >>>> > + for (InjectionPoint injectionPoint : injectionPoints) { >>>> > + Type type = injectionPoint.getType(); >>>> > >>>> > - /** >>>> > - * Get the property key to use. >>>> > - * In case the {@link ConfigProperty#name()} is empty we will try >>>> > to determine the key name from the InjectionPoint. >>>> > - */ >>>> > - private static String getConfigKey(InjectionPoint ip, >>>> > ConfigProperty configProperty) { >>>> > - String key = configProperty.name(); >>>> > - if (!key.isEmpty()) { >>>> > - return key; >>>> > - } >>>> > - if (ip.getAnnotated() instanceof AnnotatedMember) { >>>> > - AnnotatedMember member = (AnnotatedMember) >>>> > ip.getAnnotated(); >>>> > - AnnotatedType declaringType = member.getDeclaringType(); >>>> > - if (declaringType != null) { >>>> > - String[] parts = >>>> > declaringType.getJavaClass().getCanonicalName().split("\\."); >>>> > - String cn = parts[parts.length - 1]; >>>> > - parts[parts.length - 1] = >>>> > Character.toLowerCase(cn.charAt(0)) + (cn.length() > 1 ? cn.substring(1) >>>> > : ""); >>>> > - StringBuilder sb = new StringBuilder(parts[0]); >>>> > - for (int i = 1; i < parts.length; i++) { >>>> > - sb.append(".").append(parts[i]); >>>> > + // replace native types with their Wrapper types >>>> > + type = REPLACED_TYPES.getOrDefault(type, type); >>>> > + >>>> > + ConfigProperty configProperty = >>>> > injectionPoint.getAnnotated().getAnnotation(ConfigProperty.class); >>>> > + if (type instanceof Class) { >>>> > + // a direct injection of a ConfigProperty >>>> > + // that means a Converter must exist. >>>> > + String key = >>>> > ConfigInjectionBean.getConfigKey(injectionPoint, configProperty); >>>> > + if ((isDefaultUnset(configProperty.defaultValue())) >>>> > + && !config.getOptionalValue(key, (Class) >>>> > type).isPresent()) { >>>> > + deploymentProblems.add("No Config Value exists for >>>> > " + key); >>>> > } >>>> > - >>>> > - // now add the field name >>>> > - sb.append(".").append(member.getJavaMember().getName()); >>>> > - return sb.toString(); >>>> > } >>>> > } >>>> > >>>> > - throw new IllegalStateException("Could not find default name >>>> > for @ConfigProperty InjectionPoint " + ip); >>>> > - } >>>> > - >>>> > - private static boolean isDefaultUnset(String defaultValue) { >>>> > - return defaultValue == null || defaultValue.length() == 0 || >>>> > defaultValue.equals(ConfigProperty.UNCONFIGURED_VALUE); >>>> > - } >>>> > - >>>> > - private static InjectionPoint findInjectionPoint(final BeanManager >>>> > bm, final CreationalContext<?> ctx, >>>> > - >>>> > ConfigInjectionBean bean) { >>>> > - return >>>> > InjectionPoint.class.cast(bm.getInjectableReference(bean.getSimpleInjectionPoint(), >>>> > ctx)); >>>> > - } >>>> > - >>>> > - private static final class Injection { >>>> > - private final Type type; >>>> > - private final Collection<String> keys = new ArrayList<>(); >>>> > - private final Collection<String> defaultValues = new >>>> > ArrayList<>(); >>>> > - >>>> > - private Injection(final Type type) { >>>> > - this.type = type; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public boolean equals(final Object o) { >>>> > - if (this == o) { >>>> > - return true; >>>> > - } >>>> > - if (o == null || Injection.class != o.getClass()) { >>>> > - return false; >>>> > - } >>>> > - final Injection injection = Injection.class.cast(o); >>>> > - return Objects.equals(type, injection.type); >>>> > + if (!deploymentProblems.isEmpty()) { >>>> > + add.addDeploymentProblem(new DeploymentException("Error >>>> > while validating Configuration\n" >>>> > + + >>>> > String.join("\n", deploymentProblems))); >>>> > } >>>> > >>>> > - @Override >>>> > - public int hashCode() { >>>> > - return type.hashCode(); >>>> > - } >>>> > } >>>> > >>>> > - private class ParameterizedTypeImpl implements ParameterizedType { >>>> > - private final Type rawType; >>>> > - private final Type[] types; >>>> > - >>>> > - private ParameterizedTypeImpl(final Type raw, final Type... >>>> > types) { >>>> > - this.rawType = raw; >>>> > - this.types = types; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public Type[] getActualTypeArguments() { >>>> > - return types.clone(); >>>> > - } >>>> > - >>>> > - @Override >>>> > - public Type getOwnerType() { >>>> > - return null; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public Type getRawType() { >>>> > - return rawType; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public int hashCode() { >>>> > - return Arrays.hashCode(types) ^ rawType.hashCode(); >>>> > - } >>>> > - >>>> > - @Override >>>> > - public boolean equals(final Object obj) { >>>> > - if (this == obj) { >>>> > - return true; >>>> > - } >>>> > - if (ParameterizedType.class.isInstance(obj)) { >>>> > - final ParameterizedType that = >>>> > ParameterizedType.class.cast(obj); >>>> > - final Type thatRawType = that.getRawType(); >>>> > - return (rawType == null ? thatRawType == null : >>>> > rawType.equals(thatRawType)) >>>> > - && Arrays.equals(types, >>>> > that.getActualTypeArguments()); >>>> > - } >>>> > - return false; >>>> > - } >>>> > + public void shutdown(@Observes BeforeShutdown bsd) { >>>> > + DefaultConfigProvider.instance().releaseConfig(config); >>>> > + } >>>> > >>>> > - @Override >>>> > - public String toString() { >>>> > - final StringBuilder buffer = new StringBuilder(); >>>> > - buffer.append(Class.class.cast(rawType).getName()); >>>> > - final Type[] actualTypes = getActualTypeArguments(); >>>> > - if (actualTypes.length > 0) { >>>> > - buffer.append("<"); >>>> > - final int length = actualTypes.length; >>>> > - for (int i = 0; i < length; i++) { >>>> > - if (actualTypes[i] instanceof Class) { >>>> > - buffer.append(((Class<?>) >>>> > actualTypes[i]).getSimpleName()); >>>> > - } else { >>>> > - buffer.append(actualTypes[i].toString()); >>>> > - } >>>> > - if (i != actualTypes.length - 1) { >>>> > - buffer.append(","); >>>> > - } >>>> > - } >>>> > >>>> > - buffer.append(">"); >>>> > - } >>>> > - return buffer.toString(); >>>> > - } >>>> > + static boolean isDefaultUnset(String defaultValue) { >>>> > + return defaultValue.equals(ConfigProperty.UNCONFIGURED_VALUE); >>>> > } >>>> > } >>>> > >>>> > Modified: >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigInjectionBean.java >>>> > URL: >>>> > http://svn.apache.org/viewvc/geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigInjectionBean.java?rev=1804397&r1=1804396&r2=1804397&view=diff >>>> > ============================================================================== >>>> > --- >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigInjectionBean.java >>>> > (original) >>>> > +++ >>>> > geronimo/components/config/trunk/impl/src/main/java/org/apache/geronimo/config/cdi/ConfigInjectionBean.java >>>> > Tue Aug 8 10:55:56 2017 >>>> > @@ -16,49 +16,59 @@ >>>> > */ >>>> > package org.apache.geronimo.config.cdi; >>>> > >>>> > -import org.eclipse.microprofile.config.inject.ConfigProperty; >>>> > +import java.io.IOException; >>>> > +import java.io.Serializable; >>>> > +import java.lang.annotation.Annotation; >>>> > +import java.lang.reflect.ParameterizedType; >>>> > +import java.lang.reflect.Type; >>>> > +import java.util.Collections; >>>> > +import java.util.HashSet; >>>> > +import java.util.Optional; >>>> > +import java.util.Set; >>>> > >>>> > import javax.enterprise.context.Dependent; >>>> > import javax.enterprise.context.spi.CreationalContext; >>>> > -import javax.enterprise.inject.Default; >>>> > import javax.enterprise.inject.spi.Annotated; >>>> > +import javax.enterprise.inject.spi.AnnotatedMember; >>>> > +import javax.enterprise.inject.spi.AnnotatedType; >>>> > import javax.enterprise.inject.spi.Bean; >>>> > +import javax.enterprise.inject.spi.BeanManager; >>>> > import javax.enterprise.inject.spi.InjectionPoint; >>>> > import javax.enterprise.inject.spi.PassivationCapable; >>>> > import javax.enterprise.util.AnnotationLiteral; >>>> > -import java.lang.annotation.Annotation; >>>> > -import java.lang.reflect.Member; >>>> > -import java.lang.reflect.ParameterizedType; >>>> > -import java.lang.reflect.Type; >>>> > -import java.util.Collections; >>>> > -import java.util.HashSet; >>>> > -import java.util.Set; >>>> > +import javax.inject.Provider; >>>> > + >>>> > +import org.apache.geronimo.config.ConfigImpl; >>>> > +import org.eclipse.microprofile.config.Config; >>>> > +import org.eclipse.microprofile.config.ConfigProvider; >>>> > +import org.eclipse.microprofile.config.inject.ConfigProperty; >>>> > +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; >>>> > >>>> > /** >>>> > * @author <a href="mailto:[email protected]">Mark Struberg</a> >>>> > */ >>>> > -public abstract class ConfigInjectionBean<T> implements Bean<T>, >>>> > PassivationCapable { >>>> > +public class ConfigInjectionBean<T> implements Bean<T>, >>>> > PassivationCapable { >>>> > >>>> > private final static Set<Annotation> QUALIFIERS = new HashSet<>(); >>>> > static { >>>> > QUALIFIERS.add(new ConfigPropertyLiteral()); >>>> > } >>>> > >>>> > + private final BeanManager bm; >>>> > private final Class rawType; >>>> > private final Set<Type> types; >>>> > - private final String id; >>>> > - private final boolean alternative; >>>> > - >>>> > - ConfigInjectionBean(Type type) { >>>> > - this(type,false); >>>> > - } >>>> > >>>> > - ConfigInjectionBean(Type type, boolean alternative) { >>>> > - this.types = new HashSet<>(); >>>> > - this.types.add(type); >>>> > - this.rawType = getRawType(type); >>>> > - this.id = "ConfigInjectionBean_" + type.toString(); >>>> > - this.alternative = alternative; >>>> > + /** >>>> > + * only access via {@link #getConfig(} >>>> > + */ >>>> > + private Config _config; >>>> > + >>>> > + public ConfigInjectionBean(BeanManager bm, Type type) { >>>> > + this.bm = bm; >>>> > + >>>> > + types = new HashSet<>(); >>>> > + types.add(type); >>>> > + rawType = getRawType(type); >>>> > } >>>> > >>>> > private Class getRawType(Type type) { >>>> > @@ -76,7 +86,7 @@ public abstract class ConfigInjectionBea >>>> > >>>> > @Override >>>> > public Set<InjectionPoint> getInjectionPoints() { >>>> > - return Collections.emptySet(); >>>> > + return Collections.EMPTY_SET; >>>> > } >>>> > >>>> > @Override >>>> > @@ -90,8 +100,90 @@ public abstract class ConfigInjectionBea >>>> > } >>>> > >>>> > @Override >>>> > + public T create(CreationalContext<T> context) { >>>> > + Set<Bean<?>> beans = bm.getBeans(InjectionPoint.class); >>>> > + Bean<?> bean = bm.resolve(beans); >>>> > + InjectionPoint ip = (InjectionPoint) bm.getReference(bean, >>>> > InjectionPoint.class, context); >>>> > + if (ip == null) { >>>> > + throw new IllegalStateException("Could not retrieve >>>> > InjectionPoint"); >>>> > + } >>>> > + Annotated annotated = ip.getAnnotated(); >>>> > + ConfigProperty configProperty = >>>> > annotated.getAnnotation(ConfigProperty.class); >>>> > + String key = getConfigKey(ip, configProperty); >>>> > + String defaultValue = configProperty.defaultValue(); >>>> > + >>>> > + if (annotated.getBaseType() instanceof ParameterizedType) { >>>> > + ParameterizedType paramType = (ParameterizedType) >>>> > annotated.getBaseType(); >>>> > + Type rawType = paramType.getRawType(); >>>> > + >>>> > + // handle Provider<T> >>>> > + if (rawType instanceof Class && ((Class) >>>> > rawType).isAssignableFrom(Provider.class) && >>>> > paramType.getActualTypeArguments().length == 1) { >>>> > + Class clazz = (Class) >>>> > paramType.getActualTypeArguments()[0]; //X TODO check type again, etc >>>> > + return (T) new ConfigValueProvider(getConfig(), key, >>>> > clazz); >>>> > + } >>>> > + >>>> > + // handle Optional<T> >>>> > + if (rawType instanceof Class && ((Class) >>>> > rawType).isAssignableFrom(Optional.class) && >>>> > paramType.getActualTypeArguments().length == 1) { >>>> > + Class clazz = (Class) >>>> > paramType.getActualTypeArguments()[0]; //X TODO check type again, etc >>>> > + return (T) getConfig().getOptionalValue(key, clazz); >>>> > + } >>>> > + } >>>> > + else { >>>> > + Class clazz = (Class) annotated.getBaseType(); >>>> > + if (ConfigExtension.isDefaultUnset(defaultValue)) { >>>> > + return (T) getConfig().getValue(key, clazz); >>>> > + } >>>> > + else { >>>> > + Config config = getConfig(); >>>> > + return (T) config.getOptionalValue(key, clazz) >>>> > + .orElse(((ConfigImpl) >>>> > config).convert(defaultValue, clazz)); >>>> > + } >>>> > + } >>>> > + >>>> > + throw new IllegalStateException("unhandled ConfigProperty"); >>>> > + } >>>> > + >>>> > + >>>> > + /** >>>> > + * Get the property key to use. >>>> > + * In case the {@link ConfigProperty#name()} is empty we will try >>>> > to determine the key name from the InjectionPoint. >>>> > + */ >>>> > + public static String getConfigKey(InjectionPoint ip, ConfigProperty >>>> > configProperty) { >>>> > + String key = configProperty.name(); >>>> > + if (key.length() > 0) { >>>> > + return key; >>>> > + } >>>> > + if (ip.getAnnotated() instanceof AnnotatedMember) { >>>> > + AnnotatedMember member = (AnnotatedMember) >>>> > ip.getAnnotated(); >>>> > + AnnotatedType declaringType = member.getDeclaringType(); >>>> > + if (declaringType != null) { >>>> > + String[] parts = >>>> > declaringType.getJavaClass().getCanonicalName().split("\\."); >>>> > + String cn = parts[parts.length-1]; >>>> > + parts[parts.length-1] = >>>> > Character.toLowerCase(cn.charAt(0)) + (cn.length() > 1 ? cn.substring(1) >>>> > : ""); >>>> > + StringBuilder sb = new StringBuilder(parts[0]); >>>> > + for (int i = 1; i < parts.length; i++) { >>>> > + sb.append(".").append(parts[i]); >>>> > + } >>>> > + >>>> > + // now add the field name >>>> > + sb.append(".").append(member.getJavaMember().getName()); >>>> > + return sb.toString(); >>>> > + } >>>> > + } >>>> > + >>>> > + throw new IllegalStateException("Could not find default name >>>> > for @ConfigProperty InjectionPoint " + ip); >>>> > + } >>>> > + >>>> > + public Config getConfig() { >>>> > + if (_config == null) { >>>> > + _config = ConfigProvider.getConfig(); >>>> > + } >>>> > + return _config; >>>> > + } >>>> > + >>>> > + @Override >>>> > public void destroy(T instance, CreationalContext<T> context) { >>>> > - // no-op >>>> > + >>>> > } >>>> > >>>> > @Override >>>> > @@ -116,21 +208,17 @@ public abstract class ConfigInjectionBea >>>> > >>>> > @Override >>>> > public Set<Class<? extends Annotation>> getStereotypes() { >>>> > - return Collections.emptySet(); >>>> > + return Collections.EMPTY_SET; >>>> > } >>>> > >>>> > @Override >>>> > public boolean isAlternative() { >>>> > - return alternative; >>>> > + return true; >>>> > } >>>> > >>>> > @Override >>>> > public String getId() { >>>> > - return id; >>>> > - } >>>> > - >>>> > - InjectionPoint getSimpleInjectionPoint() { >>>> > - return simpleInjectionPoint; >>>> > + return "ConfigInjectionBean_" + rawType.getName(); >>>> > } >>>> > >>>> > private static class ConfigPropertyLiteral extends >>>> > AnnotationLiteral<ConfigProperty> implements ConfigProperty { >>>> > @@ -145,41 +233,31 @@ public abstract class ConfigInjectionBea >>>> > } >>>> > } >>>> > >>>> > - private final InjectionPoint simpleInjectionPoint = new >>>> > InjectionPoint() { >>>> > - >>>> > - @Override >>>> > - public boolean isTransient() { >>>> > - return false; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public boolean isDelegate() { >>>> > - return false; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public Type getType() { >>>> > - return InjectionPoint.class; >>>> > - } >>>> > - >>>> > - @Override >>>> > - public Set<Annotation> getQualifiers() { >>>> > - return Collections.singleton(new >>>> > AnnotationLiteral<Default>() {}); >>>> > - } >>>> > - >>>> > - @Override >>>> > - public Member getMember() { >>>> > - return null; >>>> > + /** >>>> > + * A special Provider<T> >>>> > + * This concrete class is needed because we need the injected >>>> > Provider for the ConfigProperty >>>> > + * to be Serializable. A Lambda would not work in this case >>>> > + */ >>>> > + public static class ConfigValueProvider<T> implements Provider<T>, >>>> > Serializable { >>>> > + private transient Config config; >>>> > + private final String key; >>>> > + private final Class<T> type; >>>> > + >>>> > + ConfigValueProvider(Config config, String key, Class<T> type) { >>>> > + this.config = config; >>>> > + this.key = key; >>>> > + this.type = type; >>>> > } >>>> > >>>> > @Override >>>> > - public Bean<?> getBean() { >>>> > - return ConfigInjectionBean.this; >>>> > + public T get() { >>>> > + return (T) config.getValue(key, type); >>>> > + } >>>> > + >>>> > + private void readObject(java.io.ObjectInputStream in) throws >>>> > IOException, ClassNotFoundException { >>>> > + in.defaultReadObject(); >>>> > + config = ConfigProviderResolver.instance().getConfig(); >>>> > } >>>> > >>>> > - @Override >>>> > - public Annotated getAnnotated() { >>>> > - return null; >>>> > - } >>>> > - }; >>>> > + } >>>> > } >>>> > >>>> > Modified: >>>> > geronimo/components/config/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProviderTest.java >>>> > URL: >>>> > http://svn.apache.org/viewvc/geronimo/components/config/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProviderTest.java?rev=1804397&r1=1804396&r2=1804397&view=diff >>>> > ============================================================================== >>>> > --- >>>> > geronimo/components/config/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProviderTest.java >>>> > (original) >>>> > +++ >>>> > geronimo/components/config/trunk/impl/src/test/java/org/apache/geronimo/config/test/internal/ProviderTest.java >>>> > Tue Aug 8 10:55:56 2017 >>>> > @@ -33,9 +33,12 @@ import org.testng.annotations.Test; >>>> > >>>> > public class ProviderTest extends Arquillian { >>>> > private static final String SOME_KEY = >>>> > "org.apache.geronimo.config.test.internal.somekey"; >>>> > + private static final String ANOTHER_KEY = >>>> > "org.apache.geronimo.config.test.internal.anotherkey"; >>>> > >>>> > @Deployment >>>> > public static WebArchive deploy() { >>>> > + System.setProperty(SOME_KEY, "someval"); >>>> > + System.setProperty(ANOTHER_KEY, "someval"); >>>> > JavaArchive testJar = ShrinkWrap >>>> > .create(JavaArchive.class, "configProviderTest.jar") >>>> > .addClasses(ProviderTest.class, SomeBean.class) >>>> > @@ -70,8 +73,16 @@ public class ProviderTest extends Arquil >>>> > @ConfigProperty(name=SOME_KEY) >>>> > private Provider<String> myconfig; >>>> > >>>> > + @Inject >>>> > + @ConfigProperty(name=ANOTHER_KEY) >>>> > + private Provider<String> anotherconfig; >>>> > + >>>> > public String getMyconfig() { >>>> > return myconfig.get(); >>>> > } >>>> > + >>>> > + public Provider<String> getAnotherconfig() { >>>> > + return anotherconfig; >>>> > + } >>>> > } >>>> > } >>>> > >>>> > Modified: geronimo/components/config/trunk/pom.xml >>>> > URL: >>>> > http://svn.apache.org/viewvc/geronimo/components/config/trunk/pom.xml?rev=1804397&r1=1804396&r2=1804397&view=diff >>>> > ============================================================================== >>>> > --- geronimo/components/config/trunk/pom.xml (original) >>>> > +++ geronimo/components/config/trunk/pom.xml Tue Aug 8 10:55:56 2017 >>>> > @@ -27,7 +27,7 @@ >>>> > </parent> >>>> > >>>> > <groupId>org.apache.geronimo.config</groupId> >>>> > - <artifactId>config-parent</artifactId> >>>> > + <artifactId>geronimo-config</artifactId> >>>> > <version>1.1-SNAPSHOT</version> >>>> > <packaging>pom</packaging> >>>> > <name>Geronimo Microprofile Configuration</name> >>>> > @@ -50,7 +50,7 @@ >>>> > <properties> >>>> > <maven.compiler.source>1.8</maven.compiler.source> >>>> > <maven.compiler.target>1.8</maven.compiler.target> >>>> > - >>>> > <microprofile-config.version>1.1-SNAPSHOT</microprofile-config.version> >>>> > + <microprofile-config.version>1.0</microprofile-config.version> >>>> > <owb.version>1.7.3</owb.version> >>>> > <owb2.version>2.0.1-SNAPSHOT</owb2.version> >>>> > <arquillian.version>1.1.13.Final</arquillian.version> >>>> > >>>> > >>>> >>>>
