All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.inftec.ju.ee.cdi.DynamicCdiLoader Maven / Gradle / Ivy

There is a newer version: 6.1-S-5
Show newest version
package ch.inftec.ju.ee.cdi;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.inftec.ju.util.AssertUtil;
import ch.inftec.ju.util.JuRuntimeException;
import ch.inftec.ju.util.JuUtils;
import ch.inftec.ju.util.ReflectUtils;

/**
 * Helper class to load CDI implementations dynamically at run time using
 * system properties to configure them.
 * 

* To 'activate' DynamicCdiLoader in a CDI project, add a new class that extends from it in a beans enabled * module. *

* Implementations to be found by the DynamicCdiLoader must implement the tag interface DynamicCdi. They should * implement another interface for the Service they implement (so we can have different implementations at all). *

* They can optionally define an annotation DynamicCdiTag with a String value. If they do, this implementation * will be loaded if the tag is activated. *

* A tag can be activated either by specify a property with the full class name as its key and the tag as its * value or by specifying the default tag using the property ju.ee.cdi.defaultDynamicCdiTag *

* If no tag is specified, the default value '-' will be used which stands for the default implementation. * @author [email protected] *

* Important:Every dynamic implementation has to extend the interface DynamicCdi AND to specify a tag * DynamicCdiTag. We need the tag to avoid duplicate declaration issues with weld. When we use a producer to * lookup the actual implementation, it will not have a tag thus making the declaration unambiguous. * */ @ApplicationScoped public class DynamicCdiLoader { private Logger logger = LoggerFactory.getLogger(DynamicCdiLoader.class); /** * Have CDI inject all implementations that are eligable for dynamic config. *

* Note: This will not work if we have to create an instance manually using the constructor. In this case, * we need to have it injected in a managed Bean and passed to the loader using it's non-default constructor. */ @Inject @Any private Instance dynamicCdis; public DynamicCdiLoader() { // Default constructor needed by Weld } /** * A Producer of DynamicCdiLoader will need to get Instance<DynamicCdi> injected * by Weld using the annotations @Inject @Any * * @param dynamicCdis */ public DynamicCdiLoader(Instance dynamicCdis) { this.dynamicCdis = dynamicCdis; } /** * Gets the specified implementation for the given class. *

* This method is mainly public so we can test it from the esw-test1 package. Normally, it * will not be called from outside. * * @param clazz * Clazz to get implementation for (i.e. CDI instance with the appropriate SimulatorTag) * @return Instance of clazz * @throws JuRuntimeException * If no implementation can be found */ public T getImplementation(Class clazz) { AssertUtil.assertNotNull("Dynamic CDIs must be set or injected", this.dynamicCdis); // Get the tag for the specified class String tagName = JuUtils.getJuPropertyChain().get(clazz.getName(), false); String defaultTagName = JuUtils.getJuPropertyChain().get("ju.ee.cdi.defaultDynamicCdiTag", "-"); logger.debug("Looking for implementation of class {} (tag={}, defaultTag={})", clazz, tagName, defaultTagName); Map> implementations = new HashMap<>(); for (DynamicCdi simulatable : dynamicCdis) { // Handle factories if (DynamicCdiFactory.class.isAssignableFrom(simulatable.getClass())) { for (Method method : ReflectUtils.getDeclaredMethodsByAnnotation(simulatable.getClass(), DynamicCdiTag.class)) { if (method.getReturnType() == null || method.getParameterTypes().length > 0) { logger.warn("Illegal DynamicCdiFactory method. Must return a type and not have arguments: " + method); } else { if (clazz.isAssignableFrom(method.getReturnType())) { // Found match final DynamicCdi factory = simulatable; final Method factoryMethod = method; this.addType(clazz , implementations , method.getAnnotation(DynamicCdiTag.class) , new TypeCreator() { @Override public T createType() { try { @SuppressWarnings("unchecked") T t = (T) factoryMethod.invoke(factory, (Object[])null); return t; } catch (Exception ex) { throw new JuRuntimeException("Couldn't invoke method " + factoryMethod, ex); } } @Override public String toString() { return factoryMethod.toString(); } }); } } } } else { if (clazz.isAssignableFrom(simulatable.getClass())) { @SuppressWarnings("unchecked") final T type = (T)simulatable; this.addType(clazz , implementations , type.getClass().getAnnotation(DynamicCdiTag.class) , new TypeCreator() { @Override public T createType() { return type; } @Override public String toString() { return type.getClass().getName(); } }); } } } if (implementations.containsKey(tagName)) { logger.debug("Returning intance by tag match: {}", implementations.get(tagName)); return implementations.get(tagName).createType(); } else if (implementations.containsKey(defaultTagName)) { logger.debug("Returning default instance: {}", implementations.get(defaultTagName)); return implementations.get(defaultTagName).createType(); } else { throw new JuRuntimeException(String.format("No dynamic implementation found for %s and tagName=%s or defaultTagName=%s", clazz.getName(), tagName, defaultTagName)); } } private void addType(Class clazz, Map> implementations, DynamicCdiTag tag, TypeCreator typeCreator) { String tagValue = tag != null ? tag.value() : "-"; logger.debug("Found implementation: {} (tag={})", typeCreator.toString(), tagValue); if (implementations.containsKey(tagValue)) { throw new JuRuntimeException(String.format( "Found two Simulatable implementations for %s and tag=%s: %s and %s (more might exist)", clazz.getName(), tagValue, implementations.get(tagValue).toString(), typeCreator.toString())); } else { implementations.put(tagValue, typeCreator); } } /** * Helper interface to postpone object creation until we actually need one. * @author [email protected] * * @param */ private interface TypeCreator { T createType(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy