package io.crnk.cdi.internal; import io.crnk.core.engine.error.ExceptionMapper; import io.crnk.core.engine.internal.utils.ClassUtils; import io.crnk.core.module.discovery.ServiceDiscovery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; import javax.enterprise.util.TypeLiteral; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; /** * CDI-based discovery of services. * <p> * By default makes use of CDI.current() to get a BeanManager instance. An additional setter allows to set a custom * BeanManager. */ public class CdiServiceDiscovery implements ServiceDiscovery { private static final Logger LOGGER = LoggerFactory.getLogger(CdiServiceDiscovery.class); private BeanManager beanManager; private Boolean cdiAvailable = null; public void setBeanManager(BeanManager beanManager) { this.beanManager = beanManager; } public BeanManager getBeanManager() { if (beanManager != null) { return beanManager; } else if (cdiAvailable != Boolean.FALSE) { try { CDI<Object> current = CDI.current(); cdiAvailable = Boolean.TRUE; return current.getBeanManager(); } catch (IllegalStateException e) { LOGGER.error("CDI context not available, CdiServiceDiscovery will not be used"); LOGGER.debug("CDI.current() failed", e); cdiAvailable = Boolean.FALSE; return null; } } return null; } @SuppressWarnings("unchecked") @Override public <T> List<T> getInstancesByType(Class<T> clazz) { BeanManager beanManager = getBeanManager(); List<T> list = new ArrayList<>(); if (beanManager != null) { Type type = clazz; if (clazz == ExceptionMapper.class) { TypeLiteral<ExceptionMapper<?>> typeLiteral = new TypeLiteral<ExceptionMapper<?>>() { }; type = typeLiteral.getType(); } Set<Bean<?>> beans = beanManager.getBeans(type); for (Bean<?> bean : beans) { CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean); T object = (T) beanManager.getReference(bean, type, creationalContext); list.add(object); } } return list; } @Override public <A extends Annotation> List<Object> getInstancesByAnnotation(Class<A> annotationClass) { List<Object> list = new ArrayList<>(); BeanManager beanManager = getBeanManager(); if (beanManager != null) { Set<Bean<?>> beans = beanManager.getBeans(Object.class); for (Bean<?> bean : beans) { Class<?> beanClass = bean.getBeanClass(); Optional<A> annotation = ClassUtils.getAnnotation(beanClass, annotationClass); if (annotation.isPresent()) { CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean); Object object = beanManager.getReference(bean, beanClass, creationalContext); list.add(object); } } } return list; } }