Author: rmannibucau
Date: Tue Jul 4 10:02:52 2017
New Revision: 1800744
URL: http://svn.apache.org/viewvc?rev=1800744&view=rev
Log:
trying to optimize a bit the CDI integration, can need some spec updates since it looks designed to be very lazy whereas it would be trivial to make it eager for most of it
Modified:
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
Modified: geronimo/components/config/trunk/impl/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/components/config/trunk/impl/pom.xml?rev=1800744&r1=1800743&r2=1800744&view=diff
==============================================================================
--- geronimo/components/config/trunk/impl/pom.xml (original)
+++ geronimo/components/config/trunk/impl/pom.xml Tue Jul 4 10:02:52 2017
@@ -103,6 +103,7 @@
1
true
+ false
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=1800744&r1=1800743&r2=1800744&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 Jul 4 10:02:52 2017
@@ -16,90 +16,383 @@
*/
package org.apache.geronimo.config.cdi;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
+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 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.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.Function;
+import java.util.stream.Stream;
-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;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static org.eclipse.microprofile.config.ConfigProvider.getConfig;
/**
* @author Mark Struberg
*/
public class ConfigExtension implements Extension {
+ private static final Object NULL = new Object();
+
private Config config;
+ private ConfigProviderResolver resolver;
+
+ private Set injections = new HashSet<>();
+ private List deploymentProblems = new ArrayList<>();
- private Set injectionPoints = new HashSet<>();
+ void init(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, final BeanManager bm) {
+ resolver = ConfigProviderResolver.instance();
+ config = getConfig();
+ }
public void collectConfigProducer(@Observes ProcessInjectionPoint, ?> pip) {
- ConfigProperty configProperty = pip.getInjectionPoint().getAnnotated().getAnnotation(ConfigProperty.class);
+ final InjectionPoint injectionPoint = pip.getInjectionPoint();
+ final ConfigProperty configProperty = injectionPoint.getAnnotated().getAnnotation(ConfigProperty.class);
if (configProperty != null) {
- injectionPoints.add(pip.getInjectionPoint());
+ Injection injection = new Injection(injectionPoint.getType());
+ 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("Missing converter 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);
+ }
+ }
+ }
}
}
public void registerConfigProducer(@Observes AfterBeanDiscovery abd, BeanManager bm) {
- Set types = injectionPoints.stream()
- .filter(ip -> ip.getType() instanceof Class)
- .map(ip -> (Class) 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);
-
- types.forEach(type -> abd.addBean(new ConfigInjectionBean(bm, type)));
+ injections.stream()
+ .flatMap(injection -> {
+ final Function, String> keyProvider;
+ if (injection.keys.size() == 1) {
+ final String key = injection.keys.iterator().next();
+ keyProvider = ctx -> key;
+ } else {
+ keyProvider = ctx -> getName(findInjectionPoint(bm, ctx));
+ }
+
+ 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 where T is a Class"));
+ return Stream.empty();
+ }
+ final Class> providerType = Class.class.cast(paramType.getActualTypeArguments()[0]);
+ return Stream.of(new ConfigInjectionBean>(injection.type) {
+ @Override
+ public Provider> create(final CreationalContext> context) {
+ return () -> config.getValue(keyProvider.apply(context), 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 where T is a Class"));
+ return null;
+ }
+ final Class> optionalType = Class.class.cast(paramType.getActualTypeArguments()[0]);
+ return Stream.of(new ConfigInjectionBean>(injection.type) {
+ @Override
+ public Optional> create(final CreationalContext> context) {
+ return config.getOptionalValue(keyProvider.apply(context), 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