TAMAYA-238: Fxed tests.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/commit/346a4f38 Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/tree/346a4f38 Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/diff/346a4f38 Branch: refs/heads/master Commit: 346a4f38744cff476e8753913d56b34cd368c520 Parents: 9050e02 Author: anatole <anat...@apache.org> Authored: Sun Mar 19 23:05:24 2017 +0100 Committer: anatole <anat...@apache.org> Committed: Sun Mar 19 23:05:24 2017 +0100 ---------------------------------------------------------------------- .../javax.enterprise.inject.spi.Extension | 20 - modules/injection/cdi/bnd.bnd | 4 +- modules/injection/cdi/pom.xml | 63 --- .../tamaya/cdi/CDIAwareServiceContext.java | 206 ++++++++ .../apache/tamaya/cdi/CDIConfiguredField.java | 77 +++ .../apache/tamaya/cdi/CDIConfiguredMethod.java | 77 +++ .../apache/tamaya/cdi/CDIConfiguredType.java | 94 ++++ .../tamaya/cdi/ConfigurationExtension.java | 290 +++++++++++ .../tamaya/cdi/ConfigurationProducer.java | 162 ++++++ .../apache/tamaya/cdi/DefaultDynamicValue.java | 497 +++++++++++++++++++ .../tamaya/cdi/ServiceLoaderServiceContext.java | 190 +++++++ .../apache/tamaya/cdi/TamayaCDIAccessor.java | 52 ++ .../tamaya/cdi/TamayaCDIInjectionExtension.java | 277 +++++++++++ .../tamaya/cdi/TamayaSEInjectionExtension.java | 109 ++++ .../cdi/extra/ConfiguredVetoExtension.java | 43 ++ .../integration/cdi/CDIAwareServiceContext.java | 206 -------- .../integration/cdi/CDIConfiguredField.java | 77 --- .../integration/cdi/CDIConfiguredMethod.java | 77 --- .../integration/cdi/CDIConfiguredType.java | 94 ---- .../integration/cdi/ConfigurationExtension.java | 290 ----------- .../integration/cdi/ConfigurationProducer.java | 253 ---------- .../integration/cdi/DefaultDynamicValue.java | 497 ------------------- .../cdi/ServiceLoaderServiceContext.java | 190 ------- .../integration/cdi/TamayaCDIAccessor.java | 52 -- .../cdi/TamayaCDIInjectionExtension.java | 277 ----------- .../cdi/TamayaSEInjectionExtension.java | 108 ---- .../cdi/extra/ConfiguredVetoExtension.java | 43 -- .../javax.enterprise.inject.spi.Extension | 6 +- .../org.apache.tamaya.spi.ServiceContext | 2 +- ...onfigurationProducerFailedInjectionTest.java | 33 ++ .../tamaya/cdi/ConfigurationProducerTest.java | 171 +++++++ .../tamaya/cdi/ConfigurationResolverTest.java | 112 +++++ .../org/apache/tamaya/cdi/ConfiguredClass.java | 111 +++++ .../org/apache/tamaya/cdi/ConfiguredTest.java | 83 ++++ .../org/apache/tamaya/cdi/EnvironmentsTest.java | 83 ++++ .../org/apache/tamaya/cdi/InjectedClass.java | 62 +++ .../apache/tamaya/cdi/InterpolationTest.java | 62 +++ .../apache/tamaya/cdi/NotFoundNoDefault.java | 78 +++ .../tamaya/cdi/cfg/ProvidedPropertySource.java | 66 +++ .../tamaya/cdi/cfg/TestConfigProvider.java | 45 ++ .../tamaya/cdi/cfg/TestPropertySource.java | 82 +++ ...onfigurationProducerFailedInjectionTest.java | 33 -- .../cdi/ConfigurationProducerTest.java | 169 ------- .../cdi/ConfigurationResolverTest.java | 112 ----- .../tamaya/integration/cdi/ConfiguredClass.java | 111 ----- .../tamaya/integration/cdi/ConfiguredTest.java | 83 ---- .../integration/cdi/EnvironmentsTest.java | 83 ---- .../tamaya/integration/cdi/InjectedClass.java | 62 --- .../integration/cdi/InterpolationTest.java | 62 --- .../integration/cdi/NotFoundNoDefault.java | 78 --- .../cdi/cfg/ProvidedPropertySource.java | 66 --- .../integration/cdi/cfg/TestConfigProvider.java | 45 -- .../integration/cdi/cfg/TestPropertySource.java | 82 --- .../javax.enterprise.inject.spi.Extension | 20 + 54 files changed, 3088 insertions(+), 3239 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi-se/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension ---------------------------------------------------------------------- diff --git a/modules/injection/cdi-se/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/modules/injection/cdi-se/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension deleted file mode 100644 index 8580a42..0000000 --- a/modules/injection/cdi-se/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension +++ /dev/null @@ -1,20 +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.integration.cdi.TamayaCDIIntegration -org.apache.tamaya.integration.cdi.SEInjectorCDIExtension http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/bnd.bnd ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/bnd.bnd b/modules/injection/cdi/bnd.bnd index a97720d..08b9719 100644 --- a/modules/injection/cdi/bnd.bnd +++ b/modules/injection/cdi/bnd.bnd @@ -1,3 +1,3 @@ Export-Package: \ - org.apache.tamaya.inject -Bundle-SymbolicName: org.apache.tamaya.inject-ee \ No newline at end of file + org.apache.tamaya.cdi +Bundle-SymbolicName: org.apache.tamaya.cdi \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/pom.xml ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/pom.xml b/modules/injection/cdi/pom.xml index 24e4ef0..a0415b1 100644 --- a/modules/injection/cdi/pom.xml +++ b/modules/injection/cdi/pom.xml @@ -126,69 +126,6 @@ under the License. </dependency> </dependencies> <profiles> - <!--<profile>--> - <!--<id>OWB</id>--> - <!--<!– there is an issue with this profile:--> - <!--java.lang.NoClassDefFoundError: org/apache/webbeans/event/EventMetadata--> - <!--–>--> - <!--<activation>--> - <!--<activeByDefault>false</activeByDefault>--> - <!--</activation>--> - <!--<dependencies>--> - <!--<!– OWB specific dependencies–>--> - <!--<dependency>--> - <!--<groupId>org.apache.geronimo.specs</groupId>--> - <!--<artifactId>geronimo-atinject_1.0_spec</artifactId>--> - <!--<version>${geronimo-atinject-1.0-spec.version}</version>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.apache.geronimo.specs</groupId>--> - <!--<artifactId>geronimo-interceptor_1.2_spec</artifactId>--> - <!--<version>${geronimo-interceptor-1.2-spec.version}</version>--> - <!--<scope>test</scope>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.apache.geronimo.specs</groupId>--> - <!--<artifactId>geronimo-annotation_1.2_spec</artifactId>--> - <!--<version>1.0</version>--> - <!--<scope>test</scope>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.apache.geronimo.specs</groupId>--> - <!--<artifactId>geronimo-el_2.2_spec</artifactId>--> - <!--<version>1.0.2</version>--> - <!--</dependency>--> - - <!--<dependency>--> - <!--<groupId>org.apache.openwebbeans</groupId>--> - <!--<artifactId>openwebbeans-impl</artifactId>--> - <!--<version>${owb.version}</version>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.apache.openwebbeans</groupId>--> - <!--<artifactId>openwebbeans-spi</artifactId>--> - <!--<version>${owb.version}</version>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.apache.openwebbeans</groupId>--> - <!--<artifactId>openwebbeans-resource</artifactId>--> - <!--<version>${owb.version}</version>--> - <!--</dependency>--> - - <!--<dependency>--> - <!--<groupId>org.apache.bval</groupId>--> - <!--<artifactId>bval-jsr303</artifactId>--> - <!--<version>${bval.version}</version>--> - <!--<scope>test</scope>--> - <!--</dependency>--> - <!--<dependency>--> - <!--<groupId>org.apache.deltaspike.cdictrl</groupId>--> - <!--<artifactId>deltaspike-cdictrl-owb</artifactId>--> - <!--<version>${ds.version}</version>--> - <!--<scope>test</scope>--> - <!--</dependency>--> - <!--</dependencies>--> - <!--</profile>--> <profile> <id>Weld</id> <activation> http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIAwareServiceContext.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIAwareServiceContext.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIAwareServiceContext.java new file mode 100644 index 0000000..f367f71 --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIAwareServiceContext.java @@ -0,0 +1,206 @@ +/* + * 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.cdi; + +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.spi.ServiceContext; + +import javax.annotation.Priority; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import java.io.IOException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * <p>This class implements a {@link ServiceContext}, which basically provides a similar loading mechanism as used + * by the {@link ServiceLoader}. Whereas the {@link ServiceLoader} only loads configurations + * and instances from one classloader, this loader manages configs found and the related instances for each + * classloader along the classloader hierarchies individually. It ensures instances are loaded on the classloader + * level, where they first are visible. Additionally it ensures the same configuration resource (and its + * declared services) are loaded multiple times, when going up the classloader hierarchy.</p> + * + * <p>Finally classloaders are not stored by reference by this class, to ensure they still can be garbage collected. + * Refer also the inherited parent class for further details.</p> + * + * <p>This class uses an ordinal of {@code 10}, so it overrides any default {@link ServiceContext} implementations + * provided with the Tamaya core modules.</p> + */ +public class CDIAwareServiceContext implements ServiceContext { + + /** + * Singletons. + */ + private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>(); + + private ServiceContext defaultServiceContext = new ServiceLoaderServiceContext(); + + + @Override + public <T> T getService(Class<T> serviceType) { + Object cached = singletons.get(serviceType); + if (cached == null) { + Collection<T> services = getServices(serviceType); + if (services.isEmpty()) { + cached = null; + } else { + cached = getServiceWithHighestPriority(services, serviceType); + } + if(cached!=null) { + singletons.put(serviceType, cached); + } + } + return serviceType.cast(cached); + } + + @Override + public <T> T create(Class<T> serviceType) { + T serv = getService(serviceType); + if(serv!=null){ + try { + return (T)serv.getClass().newInstance(); + } catch (Exception e) { + Logger.getLogger(getClass().getName()) + .log(Level.SEVERE, "Failed to create new instance of: " +serviceType.getName(), e); + } + } + return null; + } + + /** + * Loads and registers services. + * + * @param <T> the concrete type. + * @param serviceType The service type. + * @return the items found, never {@code null}. + */ + @Override + public <T> List<T> getServices(final Class<T> serviceType) { + List<T> found = defaultServiceContext.getServices(serviceType); + BeanManager beanManager = TamayaCDIAccessor.getBeanManager(); + Instance<T> cdiInstances = null; + if(beanManager!=null){ + Set<Bean<?>> instanceBeans = beanManager.getBeans(Instance.class); + Bean<?> bean = instanceBeans.iterator().next(); + cdiInstances = (Instance<T>)beanManager.getReference(bean, Instance.class, + beanManager.createCreationalContext(bean)); + } + if(cdiInstances!=null){ + for(T t:cdiInstances.select(serviceType)){ + found.add(t); + } + } + return found; + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException { + if(cl==null){ + cl = Thread.currentThread().getContextClassLoader(); + } + if(cl==null){ + cl = getClass().getClassLoader(); + } + return cl.getResources(resource); + } + + @Override + public URL getResource(String resource, ClassLoader cl) { + if(cl==null){ + cl = Thread.currentThread().getContextClassLoader(); + } + if(cl==null){ + cl = getClass().getClassLoader(); + } + return cl.getResource(resource); + } + + /** + * Checks the given instance for a @Priority annotation. If present the annotation's value s evaluated. If no such + * annotation is present, a default priority is returned (1); + * @param o the instance, not null. + * @return a priority, by default 1. + */ + public static int getPriority(Object o){ + int prio = 1; //X TODO discuss default priority + Priority priority = o.getClass().getAnnotation(Priority.class); + if (priority != null) { + prio = priority.value(); + } + return prio; + } + + /** + * @param services to scan + * @param <T> type of the service + * + * @return the service with the highest {@link Priority#value()} + * + * @throws ConfigException if there are multiple service implementations with the maximum priority + */ + private <T> T getServiceWithHighestPriority(Collection<T> services, Class<T> serviceType) { + + // we do not need the priority stuff if the list contains only one element + if (services.size() == 1) { + return services.iterator().next(); + } + + Integer highestPriority = null; + int highestPriorityServiceCount = 0; + T highestService = null; + + for (T service : services) { + int prio = getPriority(service); + if (highestPriority == null || highestPriority < prio) { + highestService = service; + highestPriorityServiceCount = 1; + highestPriority = prio; + } else if (highestPriority == prio) { + highestPriorityServiceCount++; + } + } + + if (highestPriorityServiceCount > 1) { + throw new ConfigException(MessageFormat.format("Found {0} implementations for Service {1} with Priority {2}: {3}", + highestPriorityServiceCount, + serviceType.getName(), + highestPriority, + services)); + } + + return highestService; + } + + /** + * Returns ordinal of 20, overriding defaults as well as the inherited (internally used) CLAwareServiceContext + * instance. + * @return ordinal of 20. + */ + @Override + public int ordinal() { + return 20; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredField.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredField.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredField.java new file mode 100644 index 0000000..3331d9a --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredField.java @@ -0,0 +1,77 @@ +/* + * 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.cdi; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.inject.spi.ConfiguredField; + +import javax.enterprise.inject.spi.InjectionPoint; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * CDI implementation for event publishing of configured instances. + */ +class CDIConfiguredField implements ConfiguredField{ + + private final Field field; + private List<String> keys = new ArrayList<>(); + + CDIConfiguredField(InjectionPoint injectionPoint, List<String> keys){ + this.field = (Field)injectionPoint.getMember(); + this.keys.addAll(keys); + this.keys = Collections.unmodifiableList(this.keys); + } + + @Override + public Class<?> getType() { + return field.getType(); + } + + @Override + public Collection<String> getConfiguredKeys() { + return keys; + } + + @Override + public Field getAnnotatedField() { + return field; + } + + @Override + public String getName() { + return field.getName(); + } + + @Override + public String getSignature() { + return getName()+':'+field.getType().getName(); + } + + @Override + public void configure(Object instance, Configuration config) { + throw new UnsupportedOperationException("Use CDI annotations for configuration injection."); + } + + @Override + public String toString() { + return "CDIConfiguredField["+getSignature()+']'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredMethod.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredMethod.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredMethod.java new file mode 100644 index 0000000..e7f30f5 --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredMethod.java @@ -0,0 +1,77 @@ +/* + * 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.cdi; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.inject.spi.ConfiguredMethod; + +import javax.enterprise.inject.spi.InjectionPoint; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Implementation of a configured methods for CDI module. + */ +public class CDIConfiguredMethod implements ConfiguredMethod{ + + private final Method method; + private List<String> keys = new ArrayList<>(); + + CDIConfiguredMethod(InjectionPoint injectionPoint, List<String> keys){ + this.method = (Method)injectionPoint.getMember(); + this.keys.addAll(keys); + this.keys = Collections.unmodifiableList(this.keys); + } + + @Override + public Collection<String> getConfiguredKeys() { + return keys; + } + + @Override + public Class<?>[] getParameterTypes() { + return method.getParameterTypes(); + } + + @Override + public Method getAnnotatedMethod() { + return method; + } + + @Override + public String getName() { + return method.getName(); + } + + @Override + public String getSignature() { + return null; + } + + @Override + public void configure(Object instance, Configuration config) { + throw new UnsupportedOperationException("Use CDI annotations for configuration injection."); + } + + @Override + public String toString() { + return "CDIConfiguredMethod["+getSignature()+']'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredType.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredType.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredType.java new file mode 100644 index 0000000..901b88f --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/CDIConfiguredType.java @@ -0,0 +1,94 @@ +/* + * 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.cdi; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.inject.spi.ConfiguredField; +import org.apache.tamaya.inject.spi.ConfiguredMethod; +import org.apache.tamaya.inject.spi.ConfiguredType; + +import javax.enterprise.inject.spi.InjectionPoint; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Event published for items configured by CDI extensions. This is for example used by the documentation module + * to automatically track the configuration endpoints for documentation. + */ +class CDIConfiguredType implements ConfiguredType{ + + private final Class<?> type; + private final List<CDIConfiguredMethod> methods = new ArrayList<>(); + private final List<CDIConfiguredField> fields = new ArrayList<>(); + + public CDIConfiguredType(Class<?> type){ + this.type = Objects.requireNonNull(type); + } + + @Override + public Class getType() { + return type; + } + + @Override + public String getName() { + return type.getName(); + } + + @Override + public Collection<ConfiguredField> getConfiguredFields() { + return null; + } + + @Override + public Collection<ConfiguredMethod> getConfiguredMethods() { + return null; + } + + @Override + public void configure(Object instance, Configuration config) { + throw new UnsupportedOperationException("Use CDI annotations for configuration injection."); + } + + /** + * Used to build up during injection point processing. + * @param injectionPoint the CDI injection ppint, not null. + * @param keys the possible config keys, in order of precedence, not null. + */ + void addConfiguredMember(InjectionPoint injectionPoint, List<String> keys) { + Member member = injectionPoint.getMember(); + if(member instanceof Field){ + this.fields.add(new CDIConfiguredField(injectionPoint, keys)); + } else if(member instanceof Method){ + this.methods.add(new CDIConfiguredMethod(injectionPoint, keys)); + } + } + + @Override + public String toString() { + return "CDIConfiguredType{" + + "type=" + type + + ", methods=" + methods + + ", fields=" + fields + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationExtension.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationExtension.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationExtension.java new file mode 100644 index 0000000..b715b78 --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationExtension.java @@ -0,0 +1,290 @@ +/* + * 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.cdi; + +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.ConfigOperator; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.ConfigurationProvider; +import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.WithConfigOperator; +import org.apache.tamaya.inject.api.WithPropertyConverter; +import org.apache.tamaya.spi.PropertyConverter; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.ProcessBean; +import javax.enterprise.inject.spi.ProcessProducerMethod; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + + +/** + * CDI Extension module that adds injection mechanism for configuration. + * + * @see org.apache.tamaya.inject.api.Config + * @see org.apache.tamaya.inject.api.ConfigDefaultSections + * @see ConfigException + */ +public class ConfigurationExtension implements Extension { + + private static final Logger LOG = Logger.getLogger(ConfigurationExtension.class.getName()); + + static final Map<Class, ConfigOperator> CUSTOM_OPERATORS = new ConcurrentHashMap<>(); + static final Map<Class, PropertyConverter> CUSTOM_CONVERTERS = new ConcurrentHashMap<>(); + + private final Set<Type> types = new HashSet<>(); + private Bean<?> convBean; + + /** + * Constructor for loading logging its load. + */ + public ConfigurationExtension(){ + LOG.finest("Loading Tamaya CDI Support..."); + } + + /** + * Method that checks the configuration injection points during deployment for available configuration. + * @param pb the bean to process. + * @param beanManager the bean manager to notify about new injections. + */ + public void retrieveTypes(@Observes final ProcessBean<?> pb, BeanManager beanManager) { + + final Set<InjectionPoint> ips = pb.getBean().getInjectionPoints(); + CDIConfiguredType configuredType = new CDIConfiguredType(pb.getBean().getBeanClass()); + + boolean configured = false; + boolean logged = false; + for (InjectionPoint injectionPoint : ips) { + if (injectionPoint.getAnnotated().isAnnotationPresent(Config.class)) { + final Config annotation = injectionPoint.getAnnotated().getAnnotation(Config.class); + final ConfigDefaultSections typeAnnot = injectionPoint.getAnnotated().getAnnotation(ConfigDefaultSections.class); + final List<String> keys = evaluateKeys(injectionPoint.getMember().getName(), + annotation!=null?annotation.value():null, + typeAnnot!=null?typeAnnot.value():null); + + final WithConfigOperator withOperatorAnnot = injectionPoint.getAnnotated().getAnnotation(WithConfigOperator.class); + if(withOperatorAnnot!=null){ + tryLoadOpererator(withOperatorAnnot.value()); + } + final WithPropertyConverter withConverterAnnot = injectionPoint.getAnnotated().getAnnotation(WithPropertyConverter.class); + if(withConverterAnnot!=null){ + tryLoadConverter(withConverterAnnot.value()); + } + + // We don't want to wait until the injection really fails at runtime. + // If there is a non resolvable configuration, we want to know at startup. + Configuration config = ConfigurationProvider.getConfiguration(); + String value = null; + for(String key:keys) { + value = config.get(key); + if(value!=null){ + break; + } + } + if(value==null && !annotation.defaultValue().isEmpty()){ + value = annotation.defaultValue(); + } + if(value==null){ + throw new ConfigException(String.format( + "Cannot resolve any of the possible configuration keys: %s. Please provide one of the given keys " + + "with a value in your configuration sources.", + keys.toString())); + } + types.add(injectionPoint.getType()); + if(annotation!=null){ + configured = true; + if(!logged) { + LOG.finest("Enabling Tamaya CDI Configuration on bean: " + configuredType.getName()); + } + configuredType.addConfiguredMember(injectionPoint, keys); + } + } + } + if(configured) { + beanManager.fireEvent(configuredType); + } + } + + + public void captureConvertBean(@Observes final ProcessProducerMethod<?, ?> ppm) { + if (ppm.getAnnotated().isAnnotationPresent(Config.class)) { + convBean = ppm.getBean(); + } + + } + + public void addConverter(@Observes final AfterBeanDiscovery abd, final BeanManager bm) { + if(!types.isEmpty()) { + abd.addBean(new ConverterBean(convBean, types)); + } + } + + private void tryLoadOpererator(Class<? extends ConfigOperator> operatorClass) { + Objects.requireNonNull(operatorClass); + if(ConfigOperator.class == operatorClass){ + return; + } + try{ + if(!CUSTOM_OPERATORS.containsKey(operatorClass)) { + CUSTOM_OPERATORS.put(operatorClass, operatorClass.newInstance()); + } + } catch(Exception e){ + throw new ConfigException("Custom ConfigOperator could not be loaded: " + operatorClass.getName(), e); + } + } + + private void tryLoadConverter(Class<? extends PropertyConverter> converterClass) { + Objects.requireNonNull(converterClass); + if(PropertyConverter.class == converterClass){ + return; + } + try{ + if(!CUSTOM_CONVERTERS.containsKey(converterClass)) { + CUSTOM_CONVERTERS.put(converterClass, converterClass.newInstance()); + } + } catch(Exception e){ + throw new ConfigException("Custom PropertyConverter could not be loaded: " + converterClass.getName(), e); + } + } + + /** + * Evaluates the effective keys to be used. if no {@code keys} are defined, {@code memberName} is used. + * The effective keys are then combined with the sections given (if any) and only, if the given keys are not + * absolute keys (surrounded by brackets). + * @param memberName the default member name, not null. + * @param keys the keys, may be empty, or null. + * @param sections the default sections, may be empty. May also be null. + * @return the list of keys to be finally used for configuration resolution in order of + * precedence. The first keys in the list that could be successfully resolved define the final + * configuration value. + */ + public static List<String> evaluateKeys(String memberName, String[] keys, String[] sections) { + List<String> effKeys = new ArrayList<>(); + if(keys!=null){ + effKeys.addAll(Arrays.asList(keys)); + } + if (effKeys.isEmpty()) { + effKeys.add(memberName); + } + ListIterator<String> iterator = effKeys.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 (sections != null && sections.length>0) { + // 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 : sections) { + iterator.add(area.isEmpty() ? next : area + '.' + next); + } + } + } + } + return effKeys; + } + + + /** + * Internally used conversion bean. + */ + private static class ConverterBean implements Bean<Object> { + + private final Bean<Object> delegate; + private final Set<Type> types; + + public ConverterBean(final Bean convBean, final Set<Type> types) { + this.types = types; + this.delegate = convBean; + } + + @Override + public Set<Type> getTypes() { + return types; + } + + @Override + public Class<?> getBeanClass() { + return delegate.getBeanClass(); + } + + @Override + public Set<InjectionPoint> getInjectionPoints() { + return delegate.getInjectionPoints(); + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public Set<Annotation> getQualifiers() { + return delegate.getQualifiers(); + } + + @Override + public Class<? extends Annotation> getScope() { + return delegate.getScope(); + } + + @Override + public Set<Class<? extends Annotation>> getStereotypes() { + return delegate.getStereotypes(); + } + + @Override + public boolean isAlternative() { + return delegate.isAlternative(); + } + + @Override + public boolean isNullable() { + return delegate.isNullable(); + } + + @Override + public Object create(CreationalContext<Object> creationalContext) { + return delegate.create(creationalContext); + } + + @Override + public void destroy(Object instance, CreationalContext<Object> creationalContext) { + delegate.destroy(instance, creationalContext); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java new file mode 100644 index 0000000..1d07c98 --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ConfigurationProducer.java @@ -0,0 +1,162 @@ +/* + * 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.cdi; + +import org.apache.tamaya.*; +import org.apache.tamaya.inject.api.*; +import org.apache.tamaya.spi.ConfigurationContext; +import org.apache.tamaya.spi.ConfigurationContextBuilder; +import org.apache.tamaya.spi.ConversionContext; +import org.apache.tamaya.spi.PropertyConverter; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.InjectionPoint; +import java.io.File; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Producer bean for configuration properties. + */ +@ApplicationScoped +public class ConfigurationProducer { + + private static final Logger LOGGER = Logger.getLogger(ConfigurationProducer.class.getName()); + + private DynamicValue createDynamicValue(final InjectionPoint injectionPoint) { + Member member = injectionPoint.getMember(); + if (member instanceof Field) { + return DefaultDynamicValue.of((Field) member, ConfigurationProvider.getConfiguration()); + } else if (member instanceof Method) { + return DefaultDynamicValue.of((Method) member, ConfigurationProvider.getConfiguration()); + } + return null; + } + + @Produces + @Config + public Object resolveAndConvert(final InjectionPoint injectionPoint) { + if (DynamicValue.class.equals(injectionPoint.getAnnotated().getBaseType())) { + return createDynamicValue(injectionPoint); + } + final Config annotation = injectionPoint.getAnnotated().getAnnotation(Config.class); + final ConfigDefaultSections typeAnnot = injectionPoint.getAnnotated().getAnnotation(ConfigDefaultSections.class); + final List<String> keys = TamayaCDIInjectionExtension.evaluateKeys(injectionPoint.getMember().getName(), + annotation != null ? annotation.value() : null, + typeAnnot != null ? typeAnnot.value() : null); + + final WithConfigOperator withOperatorAnnot = injectionPoint.getAnnotated().getAnnotation(WithConfigOperator.class); + ConfigOperator operator = null; + if (withOperatorAnnot != null) { + operator = TamayaCDIInjectionExtension.CUSTOM_OPERATORS.get(withOperatorAnnot.value()); + } + PropertyConverter customConverter = null; + final WithPropertyConverter withConverterAnnot = injectionPoint.getAnnotated().getAnnotation(WithPropertyConverter.class); + if (withConverterAnnot != null) { + customConverter = TamayaCDIInjectionExtension.CUSTOM_CONVERTERS.get(withConverterAnnot.value()); + } + + // unless the extension is not installed, this should never happen because the extension + // enforces the resolvability of the config + Configuration config = ConfigurationProvider.getConfiguration(); + if (operator != null) { + config = operator.operate(config); + } + final Class<?> toType = (Class<?>) injectionPoint.getAnnotated().getBaseType(); + String textValue = null; + String defaultTextValue = annotation.defaultValue().isEmpty() ? "" : annotation.defaultValue(); + String keyFound = null; + for (String key : keys) { + textValue = config.get(key); + if (textValue != null) { + keyFound = key; + break; + } + } + ConversionContext.Builder builder = new ConversionContext.Builder(config, + ConfigurationProvider.getConfiguration().getContext(), keyFound, TypeLiteral.of(toType)); + if (injectionPoint.getMember() instanceof AnnotatedElement) { + builder.setAnnotatedElement((AnnotatedElement) injectionPoint.getMember()); + } + ConversionContext conversionContext = builder.build(); + Object value = null; + if (keyFound != null) { + if (customConverter != null) { + value = customConverter.convert(textValue, conversionContext); + } + if (value == null) { + value = config.get(keyFound, toType); + } + } else if (defaultTextValue != null) { + value = defaultTextValue; + if (customConverter != null) { + value = customConverter.convert((String)value, conversionContext); + } + if (value != null) { + List<PropertyConverter<Object>> converters = ConfigurationProvider.getConfiguration().getContext() + .getPropertyConverters(TypeLiteral.of(toType)); + for (PropertyConverter<Object> converter : converters) { + try { + value = converter.convert(defaultTextValue, conversionContext); + if (value != null) { + LOGGER.log(Level.FINEST, "Parsed default value from '" + defaultTextValue + "' into " + + injectionPoint); + break; + } + } catch (Exception e) { + LOGGER.log(Level.FINEST, "Failed to convert default value '" + defaultTextValue + "' for " + + injectionPoint, e); + } + } + } + } + if (value == null) { + throw new ConfigException(String.format( + "Can't resolve any of the possible config keys: %s to the required target type: %s, supported formats: %s", + keys.toString(), toType.getName(), conversionContext.getSupportedFormats().toString())); + } + LOGGER.finest(String.format("Injecting %s for key %s in class %s", keyFound, value.toString(), injectionPoint.toString())); + return value; + } + + @Produces + public Configuration getConfiguration(){ + return ConfigurationProvider.getConfiguration(); + } + + @Produces + public ConfigurationContext getConfigurationContext(){ + return ConfigurationProvider.getConfiguration().getContext(); + } + + @Produces + public ConfigurationContextBuilder getConfigurationContextBuilder(){ + return ConfigurationProvider.getConfigurationContextBuilder(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/DefaultDynamicValue.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/DefaultDynamicValue.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/DefaultDynamicValue.java new file mode 100644 index 0000000..5e4692f --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/DefaultDynamicValue.java @@ -0,0 +1,497 @@ +/* + * 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.cdi; + +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.inject.api.DynamicValue; +import org.apache.tamaya.inject.api.LoadPolicy; +import org.apache.tamaya.inject.api.UpdatePolicy; +import org.apache.tamaya.inject.api.WithPropertyConverter; +import org.apache.tamaya.inject.spi.BaseDynamicValue; +import org.apache.tamaya.inject.spi.InjectionUtils; +import org.apache.tamaya.spi.ConversionContext; +import org.apache.tamaya.spi.PropertyConverter; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; +import java.util.logging.Logger; + +/** + * A accessor for a single configured value. This can be used to support values that may change during runtime, + * reconfigured or final. Hereby external code (could be Tamaya configuration listners or client code), can set a + * new value. Depending on the {@link UpdatePolicy} the new value is immedeately active or it requires an active commit + * by client code. Similarly an instance also can ignore all later changes to the value. + * <h3>Implementation Details</h3> + * This class is + * <ul> + * <li>Serializable, when also the item stored is serializable</li> + * <li>Thread safe</li> + * </ul> + * + * @param <T> The type of the value. + */ +final class DefaultDynamicValue<T> extends BaseDynamicValue<T> { + + private static final long serialVersionUID = -2071172847144537443L; + + /** + * The property name of the entry. + */ + private final String propertyName; + /** + * The keys to be resolved. + */ + private final String[] keys; + /** + * Back reference to the base configuration instance. This reference is used reevalaute the given property and + * compare the result with the previous value after a configuration change was triggered. + */ + private final Configuration configuration; + /** + * The target type of the property used to lookup a matching {@link PropertyConverter}. + * If null, {@code propertyConverter} is set and used instead. + */ + private final TypeLiteral<T> targetType; + /** + * The property converter to be applied, may be null. In the ladder case targetType is not null. + */ + private final PropertyConverter<T> propertyConverter; + /** + * Policy that defines how new values are applied, be default it is applied initially once, but never updated + * anymore. + */ + private UpdatePolicy updatePolicy; + /** + * Load policy. + */ + private final LoadPolicy loadPolicy; + + /** + * The current value, never null. + */ + private transient T value; + /** + * The new value, or null. + */ + private transient Object[] newValue; + /** + * List of listeners that listen for changes. + */ + private transient WeakList<PropertyChangeListener> listeners; + + /** + * Constructor. + * + * @param propertyName the name of the fields' property/method. + * @param keys the keys of the property, not null. + * @param configuration the configuration, not null. + * @param targetType the target type, not null. + * @param propertyConverter the optional converter to be used. + */ + private DefaultDynamicValue(String propertyName, Configuration configuration, TypeLiteral<T> targetType, + PropertyConverter<T> propertyConverter, List<String> keys, LoadPolicy loadPolicy, + UpdatePolicy updatePolicy) { + this.propertyName = Objects.requireNonNull(propertyName); + this.keys = keys.toArray(new String[keys.size()]); + this.configuration = Objects.requireNonNull(configuration); + this.propertyConverter = propertyConverter; + this.targetType = targetType; + this.loadPolicy = Objects.requireNonNull(loadPolicy); + this.updatePolicy = Objects.requireNonNull(updatePolicy); + if(loadPolicy == LoadPolicy.INITIAL){ + this.value = evaluateValue(); + } + } + + public static DynamicValue of(Field annotatedField, Configuration configuration) { + return of(annotatedField, configuration, LoadPolicy.ALWAYS, UpdatePolicy.IMMEDIATE); + } + + public static DynamicValue of(Field annotatedField, Configuration configuration, LoadPolicy loadPolicy) { + return of(annotatedField, configuration, loadPolicy, UpdatePolicy.IMMEDIATE); + } + + public static DynamicValue of(Field annotatedField, Configuration configuration, UpdatePolicy updatePolicy) { + return of(annotatedField, configuration, LoadPolicy.ALWAYS, updatePolicy); + } + + public static DynamicValue of(Field annotatedField, Configuration configuration, LoadPolicy loadPolicy, UpdatePolicy updatePolicy) { + // Check for adapter/filter + Type targetType = annotatedField.getGenericType(); + if (targetType == null) { + throw new ConfigException("Failed to evaluate target type for " + annotatedField.getDeclaringClass().getName() + + '.' + annotatedField.getName()); + } + if (targetType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) targetType; + Type[] types = pt.getActualTypeArguments(); + if (types.length != 1) { + throw new ConfigException("Failed to evaluate target type for " + annotatedField.getDeclaringClass().getName() + + '.' + annotatedField.getName()); + } + targetType = types[0]; + } + PropertyConverter<?> propertyConverter = null; + WithPropertyConverter annot = annotatedField.getAnnotation(WithPropertyConverter.class); + if (annot != null) { + try { + propertyConverter = annot.value().newInstance(); + } catch (Exception e) { + throw new ConfigException("Failed to instantiate annotated PropertyConverter on " + + annotatedField.getDeclaringClass().getName() + + '.' + annotatedField.getName(), e); + } + } + List<String> keys = InjectionUtils.getKeys(annotatedField); + return new DefaultDynamicValue(annotatedField.getName(), configuration, + TypeLiteral.of(targetType), propertyConverter, keys, loadPolicy, updatePolicy); + } + + public static DynamicValue of(Method method, Configuration configuration) { + return of(method, configuration, LoadPolicy.ALWAYS, UpdatePolicy.IMMEDIATE); + } + + public static DynamicValue of(Method method, Configuration configuration, UpdatePolicy updatePolicy) { + return of(method, configuration, LoadPolicy.ALWAYS, updatePolicy); + } + + public static DynamicValue of(Method method, Configuration configuration, LoadPolicy loadPolicy) { + return of(method, configuration, loadPolicy, UpdatePolicy.IMMEDIATE); + } + + public static DynamicValue of(Method method, Configuration configuration, LoadPolicy loadPolicy, UpdatePolicy updatePolicy) { + // Check for adapter/filter + Type targetType = method.getGenericReturnType(); + if (targetType == null) { + throw new ConfigException("Failed to evaluate target type for " + method.getDeclaringClass() + .getName() + '.' + method.getName()); + } + if (targetType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) targetType; + Type[] types = pt.getActualTypeArguments(); + if (types.length != 1) { + throw new ConfigException("Failed to evaluate target type for " + method.getDeclaringClass() + .getName() + '.' + method.getName()); + } + targetType = types[0]; + } + PropertyConverter<Object> propertyConverter = null; + WithPropertyConverter annot = method.getAnnotation(WithPropertyConverter.class); + if (annot != null) { + try { + propertyConverter = (PropertyConverter<Object>) annot.value().newInstance(); + } catch (Exception e) { + throw new ConfigException("Failed to instantiate annotated PropertyConverter on " + + method.getDeclaringClass().getName() + + '.' + method.getName(), e); + } + } + return new DefaultDynamicValue<>(method.getName(), + configuration, TypeLiteral.of(targetType), propertyConverter, InjectionUtils.getKeys(method), + loadPolicy, updatePolicy); + } + + + /** + * Commits a new value that has not been committed yet, make it the new value of the instance. On change any + * registered listeners will be triggered. + */ + public void commit() { + T oldValue = value; + value = newValue==null?null:(T)newValue[0]; + newValue = null; + informListeners(oldValue, value); + } + + private void informListeners(T value, T newValue) { + synchronized (this) { + PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, value, + newValue); + if (listeners != null) { + for (PropertyChangeListener consumer : listeners.get()) { + consumer.propertyChange(evt); + } + } + } + } + + /** + * Discards a new value that was published. No listeners will be informed. + */ + public void discard() { + newValue = null; + } + + + /** + * Access the {@link UpdatePolicy} used for updating this value. + * + * @return the update policy, never null. + */ + public UpdatePolicy getUpdatePolicy() { + return updatePolicy; + } + + /** + * Sets a new {@link UpdatePolicy}. + * + * @param updatePolicy the new policy, not null. + */ + public void setUpdatePolicy(UpdatePolicy updatePolicy) { + this.updatePolicy = Objects.requireNonNull(updatePolicy); + } + + /** + * Add a listener to be called as weak reference, when this value has been changed. + * + * @param l the listener, not null + */ + public void addListener(PropertyChangeListener l) { + if (listeners == null) { + listeners = new WeakList<>(); + } + listeners.add(l); + } + + /** + * Removes a listener to be called, when this value has been changed. + * + * @param l the listner to be removed, not null + */ + public void removeListener(PropertyChangeListener l) { + if (listeners != null) { + listeners.remove(l); + } + } + + /** + * If a value is present in this {@code DynamicValue}, returns the value, + * otherwise throws {@code ConfigException}. + * + * @return the non-null value held by this {@code Optional} + * @throws ConfigException if there is no value present + * @see DefaultDynamicValue#isPresent() + */ + public T get() { + T newLocalValue; + if(loadPolicy!=LoadPolicy.INITIAL) { + newLocalValue = evaluateValue(); + if (this.value == null) { + this.value = newLocalValue; + } + if(!Objects.equals(this.value, newLocalValue)){ + switch (updatePolicy){ + case IMMEDEATE: + case IMMEDIATE: + commit(); + break; + case EXPLCIT: + case EXPLICIT: + this.newValue = new Object[]{newLocalValue}; + break; + case LOG_ONLY: + informListeners(this.value, newLocalValue); + this.newValue = null; + break; + case NEVER: + this.newValue = null; + break; + default: + this.newValue = null; + break; + } + } + } + return value; + } + + /** + * Method to check for and apply a new value. Depending on the {@link UpdatePolicy} + * the value is immediately or deferred visible (or it may even be ignored completely). + * + * @return true, if a new value has been detected. The value may not be visible depending on the current + * {@link UpdatePolicy} in place. + */ + public boolean updateValue() { + if(this.value==null && this.newValue==null){ + this.value = evaluateValue(); + return false; + } + T newValue = evaluateValue(); + if (Objects.equals(newValue, this.value)) { + return false; + } + switch (this.updatePolicy) { + case LOG_ONLY: + Logger.getLogger(getClass().getName()).info("Discard change on " + this + ", newValue=" + newValue); + informListeners(value, newValue); + this.newValue = null; + break; + case NEVER: + this.newValue = null; + break; + case EXPLCIT: + case IMMEDEATE: + default: + this.newValue = new Object[]{newValue}; + commit(); + break; + } + return true; + } + + /** + * Evaluates the current value dynamically from the underlying configuration. + * + * @return the current actual value, or null. + */ + public T evaluateValue() { + T value = null; + + for (String key : keys) { + if (propertyConverter == null) { + value = configuration.get(key, targetType); + } else { + String source = configuration.get(key); + ConversionContext ctx = new ConversionContext.Builder(configuration, + configuration.getContext(), key, targetType).build(); + value = propertyConverter.convert(source, ctx); + } + + if (value != null) { + break; + } + } + + return value; + } + + /** + * Access a new value that has not yet been committed. + * + * @return the uncommitted new value, or null. + */ + public T getNewValue() { + T nv = newValue==null?null:(T)newValue[0]; + if (nv != null) { + return nv; + } + return null; + } + + + /** + * Serialization implementation that strips away the non serializable Optional part. + * + * @param oos the output stream + * @throws IOException if serialization fails. + */ + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.writeObject(getUpdatePolicy()); + oos.writeObject(get()); + } + + /** + * Reads an instance from the input stream. + * + * @param ois the object input stream + * @throws IOException if deserialization fails. + * @throws ClassNotFoundException + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + this.updatePolicy = (UpdatePolicy) ois.readObject(); + if (isPresent()) { + this.value = (T) ois.readObject(); + } + newValue = null; + } + + + /** + * Simple helper that allows keeping the listeners registered as weak references, hereby avoiding any + * memory leaks. + * + * @param <I> the type + */ + private class WeakList<I> { + final List<WeakReference<I>> refs = new LinkedList<>(); + + /** + * Adds a new instance. + * + * @param t the new instance, not null. + */ + void add(I t) { + refs.add(new WeakReference<>(t)); + } + + /** + * Removes a instance. + * + * @param t the instance to be removed. + */ + void remove(I t) { + synchronized (refs) { + for (Iterator<WeakReference<I>> iterator = refs.iterator(); iterator.hasNext(); ) { + WeakReference<I> ref = iterator.next(); + I instance = ref.get(); + if (instance == null || instance == t) { + iterator.remove(); + break; + } + } + } + } + + + /** + * Access a list (copy) of the current instances that were not discarded by the GC. + * + * @return the list of accessible items. + */ + public List<I> get() { + synchronized (refs) { + List<I> res = new ArrayList<>(); + for (Iterator<WeakReference<I>> iterator = refs.iterator(); iterator.hasNext(); ) { + WeakReference<I> ref = iterator.next(); + I instance = ref.get(); + if (instance == null) { + iterator.remove(); + } else { + res.add(instance); + } + } + return res; + } + } + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ServiceLoaderServiceContext.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ServiceLoaderServiceContext.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ServiceLoaderServiceContext.java new file mode 100644 index 0000000..3c04415 --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/ServiceLoaderServiceContext.java @@ -0,0 +1,190 @@ +/* + * 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.cdi; + +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.spi.ServiceContext; +import org.apache.tamaya.spisupport.PriorityServiceComparator; + +import javax.annotation.Priority; +import java.io.IOException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements the (default) {@link ServiceContext} interface and hereby uses the JDK + * {@link ServiceLoader} to load the services required. + */ +final class ServiceLoaderServiceContext implements ServiceContext { + private static final Logger LOG = Logger.getLogger(ServiceLoaderServiceContext.class.getName()); + /** + * List current services loaded, per class. + */ + private final ConcurrentHashMap<Class<?>, List<Object>> servicesLoaded = new ConcurrentHashMap<>(); + /** + * Singletons. + */ + private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>(); + private Map<Class, Class> factoryTypes = new ConcurrentHashMap<>(); + + @Override + public <T> T getService(Class<T> serviceType) { + Object cached = singletons.get(serviceType); + if (cached == null) { + cached = create(serviceType); + if(cached!=null) { + singletons.put(serviceType, cached); + } + } + return serviceType.cast(cached); + } + + @Override + public <T> T create(Class<T> serviceType) { + Class<? extends T> implType = factoryTypes.get(serviceType); + if(implType==null) { + Collection<T> services = getServices(serviceType); + if (services.isEmpty()) { + return null; + } else { + return getServiceWithHighestPriority(services, serviceType); + } + } + try { + return implType.newInstance(); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Failed to create instabce of " + implType.getName(), e); + return null; + } + } + + /** + * Loads and registers services. + * + * @param <T> the concrete type. + * @param serviceType The service type. + * @return the items found, never {@code null}. + */ + @Override + public <T> List<T> getServices(final Class<T> serviceType) { + List<T> found = (List<T>) servicesLoaded.get(serviceType); + if (found != null) { + return found; + } + List<T> services = new ArrayList<>(); + try { + for (T t : ServiceLoader.load(serviceType)) { + services.add(t); + } + Collections.sort(services, PriorityServiceComparator.getInstance()); + services = Collections.unmodifiableList(services); + } catch (ServiceConfigurationError e) { + LOG.log(Level.WARNING, + "Error loading services current type " + serviceType, e); + if(services==null){ + services = Collections.emptyList(); + } + } + final List<T> previousServices = List.class.cast(servicesLoaded.putIfAbsent(serviceType, (List<Object>) services)); + return previousServices != null ? previousServices : services; + } + + /** + * Checks the given instance for a @Priority annotation. If present the annotation's value s evaluated. If no such + * annotation is present, a default priority is returned (1); + * @param o the instance, not null. + * @return a priority, by default 1. + */ + public static int getPriority(Object o){ + int prio = 1; //X TODO discuss default priority + Priority priority = o.getClass().getAnnotation(Priority.class); + if (priority != null) { + prio = priority.value(); + } + return prio; + } + + /** + * @param services to scan + * @param <T> type of the service + * + * @return the service with the highest {@link Priority#value()} + * + * @throws ConfigException if there are multiple service implementations with the maximum priority + */ + private <T> T getServiceWithHighestPriority(Collection<T> services, Class<T> serviceType) { + T highestService = null; + // we do not need the priority stuff if the list contains only one element + if (services.size() == 1) { + highestService = services.iterator().next(); + this.factoryTypes.put(serviceType, highestService.getClass()); + return highestService; + } + + Integer highestPriority = null; + int highestPriorityServiceCount = 0; + + for (T service : services) { + int prio = getPriority(service); + if (highestPriority == null || highestPriority < prio) { + highestService = service; + highestPriorityServiceCount = 1; + highestPriority = prio; + } else if (highestPriority == prio) { + highestPriorityServiceCount++; + } + } + + if (highestPriorityServiceCount > 1) { + throw new ConfigException(MessageFormat.format("Found {0} implementations for Service {1} with Priority {2}: {3}", + highestPriorityServiceCount, + serviceType.getName(), + highestPriority, + services)); + } + this.factoryTypes.put(serviceType, highestService.getClass()); + return highestService; + } + + @Override + public int ordinal() { + return 1; + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException{ + if(cl==null){ + cl = Thread.currentThread().getContextClassLoader(); + } + return cl.getResources(resource); + } + + @Override + public URL getResource(String resource, ClassLoader cl){ + if(cl==null){ + cl = Thread.currentThread().getContextClassLoader(); + } + return cl.getResource(resource); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIAccessor.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIAccessor.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIAccessor.java new file mode 100644 index 0000000..1d7533c --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIAccessor.java @@ -0,0 +1,52 @@ +/* + * 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.cdi; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterDeploymentValidation; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Extension; + +/** + * Tamaya main integration with CDI, storing the BeanManager reference for implementation, where no + * JNDI is available or {@code java:comp/env/BeanManager} is not set correctly. + */ +public class TamayaCDIAccessor implements Extension { + /** The BeanManager references stored. */ + private static BeanManager beanManager; + + /** + * Initializes the current BeanManager with the instance passed. + * @param validation the event + * @param beanManager the BeanManager instance + */ + @SuppressWarnings("all") + public void initBeanManager(@Observes AfterDeploymentValidation validation, BeanManager beanManager){ + TamayaCDIAccessor.beanManager = beanManager; + } + + /** + * Get the current {@link BeanManager} instance. + * @return the currently used bean manager. + */ + public static BeanManager getBeanManager(){ + return beanManager; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya-extensions/blob/346a4f38/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java ---------------------------------------------------------------------- diff --git a/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java new file mode 100644 index 0000000..749c9b0 --- /dev/null +++ b/modules/injection/cdi/src/main/java/org/apache/tamaya/cdi/TamayaCDIInjectionExtension.java @@ -0,0 +1,277 @@ +/* + * 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.cdi; + +import org.apache.tamaya.ConfigException; +import org.apache.tamaya.ConfigOperator; +import org.apache.tamaya.Configuration; +import org.apache.tamaya.ConfigurationProvider; +import org.apache.tamaya.inject.api.Config; +import org.apache.tamaya.inject.api.ConfigDefaultSections; +import org.apache.tamaya.inject.api.WithConfigOperator; +import org.apache.tamaya.inject.api.WithPropertyConverter; +import org.apache.tamaya.spi.PropertyConverter; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.*; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + + +/** + * CDI Extension module that adds injection mechanism for configuration. + * + * @see Config + * @see ConfigDefaultSections + * @see ConfigException + */ +public class TamayaCDIInjectionExtension implements Extension { + + private static final Logger LOG = Logger.getLogger(TamayaCDIInjectionExtension.class.getName()); + + static final Map<Class, ConfigOperator> CUSTOM_OPERATORS = new ConcurrentHashMap<>(); + static final Map<Class, PropertyConverter> CUSTOM_CONVERTERS = new ConcurrentHashMap<>(); + + private final Set<Type> types = new HashSet<>(); + private Bean<?> convBean; + + /** + * Constructor for loading logging its load. + */ + public TamayaCDIInjectionExtension(){ + LOG.finest("Loading Tamaya CDI Support..."); + } + + /** + * Method that checks the configuration injection points during deployment for available configuration. + * @param pb the bean to process. + * @param beanManager the bean manager to notify about new injections. + */ + public void retrieveTypes(@Observes final ProcessBean<?> pb, BeanManager beanManager) { + + final Set<InjectionPoint> ips = pb.getBean().getInjectionPoints(); + CDIConfiguredType configuredType = new CDIConfiguredType(pb.getBean().getBeanClass()); + + boolean configured = false; + boolean logged = false; + for (InjectionPoint injectionPoint : ips) { + if (injectionPoint.getAnnotated().isAnnotationPresent(Config.class)) { + final Config annotation = injectionPoint.getAnnotated().getAnnotation(Config.class); + final ConfigDefaultSections typeAnnot = injectionPoint.getAnnotated().getAnnotation(ConfigDefaultSections.class); + final List<String> keys = evaluateKeys(injectionPoint.getMember().getName(), + annotation!=null?annotation.value():null, + typeAnnot!=null?typeAnnot.value():null); + + final WithConfigOperator withOperatorAnnot = injectionPoint.getAnnotated().getAnnotation(WithConfigOperator.class); + if(withOperatorAnnot!=null){ + tryLoadOpererator(withOperatorAnnot.value()); + } + final WithPropertyConverter withConverterAnnot = injectionPoint.getAnnotated().getAnnotation(WithPropertyConverter.class); + if(withConverterAnnot!=null){ + tryLoadConverter(withConverterAnnot.value()); + } + + // We don't want to wait until the injection really fails at runtime. + // If there is a non resolvable configuration, we want to know at startup. + Configuration config = ConfigurationProvider.getConfiguration(); + String value = null; + for(String key:keys) { + value = config.get(key); + if(value!=null){ + break; + } + } + if(value==null && !annotation.defaultValue().isEmpty()){ + value = annotation.defaultValue(); + } + if(value==null){ + throw new ConfigException(String.format( + "Cannot resolve any of the possible configuration keys: %s. Please provide one of the given keys " + + "with a value in your configuration sources.", + keys.toString())); + } + types.add(injectionPoint.getType()); + if(annotation!=null){ + configured = true; + if(!logged) { + LOG.finest("Enabling Tamaya CDI Configuration on bean: " + configuredType.getName()); + } + configuredType.addConfiguredMember(injectionPoint, keys); + } + } + } + if(configured) { + beanManager.fireEvent(configuredType); + } + } + + + public void captureConvertBean(@Observes final ProcessProducerMethod<?, ?> ppm) { + if (ppm.getAnnotated().isAnnotationPresent(Config.class)) { + convBean = ppm.getBean(); + } + + } + + public void addConverter(@Observes final AfterBeanDiscovery abd, final BeanManager bm) { + if(!types.isEmpty()) { + abd.addBean(new ConverterBean(convBean, types)); + } + } + + private void tryLoadOpererator(Class<? extends ConfigOperator> operatorClass) { + Objects.requireNonNull(operatorClass); + if(ConfigOperator.class == operatorClass){ + return; + } + try{ + if(!CUSTOM_OPERATORS.containsKey(operatorClass)) { + CUSTOM_OPERATORS.put(operatorClass, operatorClass.newInstance()); + } + } catch(Exception e){ + throw new ConfigException("Custom ConfigOperator could not be loaded: " + operatorClass.getName(), e); + } + } + + private void tryLoadConverter(Class<? extends PropertyConverter> converterClass) { + Objects.requireNonNull(converterClass); + if(PropertyConverter.class == converterClass){ + return; + } + try{ + if(!CUSTOM_CONVERTERS.containsKey(converterClass)) { + CUSTOM_CONVERTERS.put(converterClass, converterClass.newInstance()); + } + } catch(Exception e){ + throw new ConfigException("Custom PropertyConverter could not be loaded: " + converterClass.getName(), e); + } + } + + /** + * Evaluates the effective keys to be used. if no {@code keys} are defined, {@code memberName} is used. + * The effective keys are then combined with the sections given (if any) and only, if the given keys are not + * absolute keys (surrounded by brackets). + * @param memberName the default member name, not null. + * @param keys the keys, may be empty, or null. + * @param sections the default sections, may be empty. May also be null. + * @return the list of keys to be finally used for configuration resolution in order of + * precedence. The first keys in the list that could be successfully resolved define the final + * configuration value. + */ + public static List<String> evaluateKeys(String memberName, String[] keys, String[] sections) { + List<String> effKeys = new ArrayList<>(); + if(keys!=null){ + effKeys.addAll(Arrays.asList(keys)); + } + if (effKeys.isEmpty()) { + effKeys.add(memberName); + } + ListIterator<String> iterator = effKeys.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 (sections != null && sections.length>0) { + // 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 : sections) { + iterator.add(area.isEmpty() ? next : area + '.' + next); + } + } + } + } + return effKeys; + } + + + /** + * Internally used conversion bean. + */ + private static class ConverterBean implements Bean<Object> { + + private final Bean<Object> delegate; + private final Set<Type> types; + + public ConverterBean(final Bean convBean, final Set<Type> types) { + this.types = types; + this.delegate = convBean; + } + + @Override + public Set<Type> getTypes() { + return types; + } + + @Override + public Class<?> getBeanClass() { + return delegate.getBeanClass(); + } + + @Override + public Set<InjectionPoint> getInjectionPoints() { + return delegate.getInjectionPoints(); + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public Set<Annotation> getQualifiers() { + return delegate.getQualifiers(); + } + + @Override + public Class<? extends Annotation> getScope() { + return delegate.getScope(); + } + + @Override + public Set<Class<? extends Annotation>> getStereotypes() { + return delegate.getStereotypes(); + } + + @Override + public boolean isAlternative() { + return delegate.isAlternative(); + } + + @Override + public boolean isNullable() { + return delegate.isNullable(); + } + + @Override + public Object create(CreationalContext<Object> creationalContext) { + return delegate.create(creationalContext); + } + + @Override + public void destroy(Object instance, CreationalContext<Object> creationalContext) { + delegate.destroy(instance, creationalContext); + } + } + +}