/* * Copyright (C) 2013 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.container.weld; import org.exoplatform.commons.utils.SecurityHelper; import org.exoplatform.container.AbstractComponentAdapter; import org.exoplatform.container.AbstractInterceptor; import org.exoplatform.container.configuration.ConfigurationManager; import org.exoplatform.container.spi.ComponentAdapter; import org.exoplatform.container.spi.ContainerException; import org.exoplatform.container.spi.Interceptor; import org.exoplatform.container.xml.Component; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.jboss.weld.environment.se.Weld; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.enterprise.context.Dependent; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.Any; import javax.enterprise.inject.Default; import javax.enterprise.inject.Instance; 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.ProcessAnnotatedType; import javax.enterprise.util.AnnotationLiteral; import javax.inject.Named; import javax.inject.Singleton; /** * The implementation of an {@link Interceptor} allowing eXo Kernel to interact with a weld container * * @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a> * @version $Id$ * */ public class WeldContainer extends AbstractInterceptor { /** * The serial version UID */ private static final long serialVersionUID = -5805946626633663689L; /** * The logger */ private static final Log LOG = ExoLogger.getLogger("exo.kernel.container.ext.provider.impl.weld.v1.WeldContainer"); /** * The name of the weld package */ private static final String WELD_PACKAGE_NAME = org.jboss.weld.Container.class.getPackage().getName(); /** * The weld object allowing to initialize and stop the weld container */ private Weld weld; /** * The weld container */ private org.jboss.weld.environment.se.WeldContainer container; /** * The helper used to access to the extensions and to know if a given class is part of the scope of * {@link Weld} */ private WeldContainerHelper helper; /** * The weld container, we will use it mainly to shutdown it manually in case of a failure */ private org.jboss.weld.Container weldContainer; /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> T getComponentInstance(final Object componentKey, Class<T> bindType, boolean autoRegistration) { T result = super.getComponentInstance(componentKey, bindType, autoRegistration); if (weld != null && result == null) { if (componentKey instanceof Class<?> && !((Class<?>)componentKey).isAnnotation()) { return getInstanceOfType((Class<T>)componentKey); } else if (componentKey instanceof String) { Set<Bean<?>> beans = container.getBeanManager().getBeans(bindType, createNamed((String)componentKey)); if (beans != null && !beans.isEmpty()) { return bindType.cast(container.instance().select(beans.iterator().next().getBeanClass()).get()); } } else if (componentKey instanceof Class<?>) { final Class<? extends Annotation> annotationType = (Class<? extends Annotation>)componentKey; Annotation annotation = createAnnotation(annotationType); Set<Bean<?>> beans = container.getBeanManager().getBeans(bindType, annotation); if (beans != null && !beans.isEmpty()) { return bindType.cast(container.instance().select(beans.iterator().next().getBeanClass()).get()); } } } return result; } /** * {@inheritDoc} */ @Override public <T> T getComponentInstanceOfType(Class<T> componentType, boolean autoRegistration) { T result = super.getComponentInstanceOfType(componentType, autoRegistration); if (weld != null && result == null) { result = getInstanceOfType(componentType); } return result; } @SuppressWarnings("unchecked") private <T> T getInstanceOfType(final Class<T> componentType) { return SecurityHelper.doPrivilegedAction(new PrivilegedAction<T>() { public T run() { if (helper.isIncluded(componentType)) { Instance<T> instance = container.instance().select(componentType); if (instance != null) { if (instance.isAmbiguous()) { Set<Bean<?>> beans = container.getBeanManager().getBeans(componentType); for (Bean<?> b : beans) { if (b.getBeanClass().isAnnotationPresent(Default.class)) { instance = (Instance<T>)container.instance().select(b.getBeanClass()); break; } } } return instance.get(); } } return null; } }); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> ComponentAdapter<T> getComponentAdapter(final Object componentKey, Class<T> bindType, boolean autoRegistration) { ComponentAdapter<T> result = super.getComponentAdapter(componentKey, bindType, autoRegistration); if (weld != null && result == null) { if (componentKey instanceof Class<?> && !((Class<?>)componentKey).isAnnotation()) { return getAdapterOfType((Class<T>)componentKey); } else if (componentKey instanceof String) { Set<Bean<?>> beans = container.getBeanManager().getBeans(bindType, createNamed((String)componentKey)); if (beans != null && !beans.isEmpty()) { return createComponentAdapter(bindType, (Instance<T>)container.instance().select(beans.iterator().next().getBeanClass())); } } else if (componentKey instanceof Class<?>) { final Class<? extends Annotation> annotationType = (Class<? extends Annotation>)componentKey; Annotation annotation = createAnnotation(annotationType); Set<Bean<?>> beans = container.getBeanManager().getBeans(bindType, annotation); if (beans != null && !beans.isEmpty()) { return createComponentAdapter(bindType, (Instance<T>)container.instance().select(beans.iterator().next().getBeanClass())); } } } return result; } /** * {@inheritDoc} */ @Override public <T> ComponentAdapter<T> getComponentAdapterOfType(Class<T> componentType, boolean autoRegistration) { ComponentAdapter<T> result = super.getComponentAdapterOfType(componentType, autoRegistration); if (weld != null && result == null) { result = getAdapterOfType(componentType); } return result; } @SuppressWarnings("unchecked") private <T> ComponentAdapter<T> getAdapterOfType(Class<T> componentType) { if (helper.isIncluded(componentType)) { Instance<T> instance = container.instance().select(componentType); if (instance != null) { if (instance.isAmbiguous()) { Set<Bean<?>> beans = container.getBeanManager().getBeans(componentType); for (Bean<?> b : beans) { if (b.getBeanClass().isAnnotationPresent(Default.class)) { instance = (Instance<T>)container.instance().select(b.getBeanClass()); break; } } } return createComponentAdapter(componentType, instance); } } return null; } private <T> ComponentAdapter<T> createComponentAdapter(final Class<T> type, final Instance<T> instance) { return new AbstractComponentAdapter<T>(type, type) { /** * The serial UID */ private static final long serialVersionUID = 8230487164261120364L; public T getComponentInstance() throws ContainerException { return SecurityHelper.doPrivilegedAction(new PrivilegedAction<T>() { public T run() { return instance.get(); } }); } public boolean isSingleton() { return false; } }; } @SuppressWarnings("unchecked") private <T> ComponentAdapter<T> createComponentAdapter(final Class<T> type, final Bean<?> b) { return new AbstractComponentAdapter<T>(type, (Class<T>)b.getBeanClass()) { /** * The serial UID */ private static final long serialVersionUID = -2398896047339159840L; public T getComponentInstance() throws ContainerException { return type.cast(container.instance().select(b.getBeanClass()).get()); } public boolean isSingleton() { return Singleton.class.equals(b.getScope()); } }; } /** * {@inheritDoc} */ @Override public <T> List<ComponentAdapter<T>> getComponentAdaptersOfType(Class<T> componentType) { List<ComponentAdapter<T>> result = super.getComponentAdaptersOfType(componentType); if (weld != null) { result = new ArrayList<ComponentAdapter<T>>(result); Set<Bean<?>> beans = container.getBeanManager().getBeans(componentType); if (beans != null) { for (Bean<?> b : beans) { result.add(createComponentAdapter(componentType, b)); } } } return result; } /** * {@inheritDoc} */ @Override public <T> List<T> getComponentInstancesOfType(Class<T> componentType) throws ContainerException { List<T> result = super.getComponentInstancesOfType(componentType); if (weld != null) { Instance<T> instance = container.instance().select(componentType); if (instance != null) { for (T t : instance) { result.add(t); } } } return result; } /** * {@inheritDoc} */ @Override public void start() { ConfigurationManager cm = super.getComponentInstanceOfType(ConfigurationManager.class, false); // We check if the component has been defined in the configuration of the current container // The goal is to enable the WeldContainer only if it is needed Component component = cm.getComponent(WeldContainerHelper.class); if (component == null) { if (LOG.isDebugEnabled()) { LOG.debug("No WeldContainerHelper has been defined, thus the WeldContainer will be disabled." + " To enable the Weld Integration please define an WeldContainerHelper"); } } else { Weld weld = new Weld(); weld.addExtension(new WeldExtension(this)); WeldContainerHelper helper = super.getComponentInstanceOfType(WeldContainerHelper.class, false); List<Extension> extensions = helper.getExtensions(); if (extensions != null) { for (Extension e : extensions) { weld.addExtension(e); } } this.helper = helper; this.container = weld.initialize(); // This is an ugly hack to make sure that the BeanManagerProxy is initialized with the right Container // This is needed especially when we intend to initialize several weld containers within the same instance container.getBeanManager().getBeans(org.jboss.weld.environment.se.WeldContainer.class); this.weldContainer = org.jboss.weld.Container.instance(); this.weld = weld; LOG.info("A WeldContainer has been enabled using the WeldContainerHelper " + helper.getClass()); } super.start(); } /** * {@inheritDoc} */ @Override public void stop() { super.stop(); if (weld != null) { org.jboss.weld.Container currentContainer = org.jboss.weld.Container.available() ? org.jboss.weld.Container.instance() : null; try { weld.shutdown(); } catch (RuntimeException e) { // In case we have several weld container initialized, we will get an IllegalStateException because weld doesn't allow to have several weld containers if (LOG.isDebugEnabled()) { LOG.debug("Could not shutdown the weld container properly", e); } } if (currentContainer != weldContainer) //NOSONAR { // Clean up manually the container in case the current container is not the container corresponding to the container of this weld container weldContainer.cleanup(); } weld = null; container = null; helper = null; weldContainer = null; } } private static Annotation createAnnotation(final Class<? extends Annotation> annotationType) { return (Annotation)Proxy.newProxyInstance(annotationType.getClassLoader(), annotationType.getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) { if ("hashCode".equals(method.getName())) { return annotationType.getName().hashCode(); } else if ("equals".equals(method.getName())) { return args[0].hashCode() == annotationType.getName().hashCode() && args[0].toString().equals(annotationType.getName()); } else if ("toString".equals(method.getName())) { return annotationType.getName(); } return annotationType; } }); } private static Named createNamed(final String name) { return new Named() { public Class<? extends Annotation> annotationType() { return Named.class; } public String value() { return name; } }; } /** * {@inheritDoc} */ public String getId() { return "WeldIntegration"; } /** * We switch to a public static class to allow weld to create a proxy the class */ public static class WeldExtension implements Extension { private final WeldContainer container; public WeldExtension() { this(null); } public WeldExtension(WeldContainer container) { this.container = container; } @SuppressWarnings({"unchecked", "rawtypes"}) void afterBeanDiscovery(@Observes AfterBeanDiscovery abd, BeanManager bm) { Collection<ComponentAdapter<?>> adapters = container.delegate.getComponentAdapters(); for (ComponentAdapter<?> adapter : adapters) { abd.addBean(new ComponentAdapterBean(adapter)); } } <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) { Class<T> clazz = pat.getAnnotatedType().getJavaClass(); if (clazz.getName().startsWith(WELD_PACKAGE_NAME)) return; if (!container.helper.isIncluded(clazz)) { pat.veto(); } } } private static void bindAll(Class<?> clazz, Set<Type> types) { if (clazz == null || clazz.equals(Object.class)) return; types.add(clazz); for (Class<?> c : clazz.getInterfaces()) { bindAll(c, types); } bindAll(clazz.getSuperclass(), types); } private static class ComponentAdapterBean<T> implements Bean<T> { private final ComponentAdapter<T> adapter; private Set<Type> types; private Set<Annotation> qualifiers; public ComponentAdapterBean(ComponentAdapter<T> adapter) { this.adapter = adapter; } /** * {@inheritDoc} */ public T create(CreationalContext<T> ctx) { return adapter.getComponentInstance(); } /** * {@inheritDoc} */ public void destroy(T instance, CreationalContext<T> ctx) { ctx.release(); } /** * {@inheritDoc} */ public Set<Type> getTypes() { if (types != null) { return types; } Set<Type> types = new HashSet<Type>(); if (adapter.getComponentKey() instanceof Class<?> && !((Class<?>)adapter.getComponentKey()).isAnnotation()) { types.add((Class<?>)adapter.getComponentKey()); types.add(adapter.getComponentImplementation()); } else { bindAll(adapter.getComponentImplementation(), types); } types.add(Object.class); return this.types = types; } /** * {@inheritDoc} */ @SuppressWarnings({"serial", "unchecked"}) public Set<Annotation> getQualifiers() { if (qualifiers != null) { return qualifiers; } Set<Annotation> qualifiers = new HashSet<Annotation>(); if (adapter.getComponentKey() instanceof String) { qualifiers.add(createNamed((String)adapter.getComponentKey())); } else if (adapter.getComponentKey() instanceof Class<?> && ((Class<?>)adapter.getComponentKey()).isAnnotation()) { qualifiers.add(createAnnotation((Class<? extends Annotation>)adapter.getComponentKey())); } else { qualifiers.add(new AnnotationLiteral<Default>() { }); qualifiers.add(new AnnotationLiteral<Any>() { }); } return this.qualifiers = qualifiers; } /** * {@inheritDoc} */ public Class<? extends Annotation> getScope() { return adapter.isSingleton() ? Singleton.class : Dependent.class; } /** * {@inheritDoc} */ public String getName() { return null; } /** * {@inheritDoc} */ public Set<Class<? extends Annotation>> getStereotypes() { return Collections.emptySet(); } /** * {@inheritDoc} */ public boolean isAlternative() { return false; } /** * {@inheritDoc} */ public Class<?> getBeanClass() { return adapter.getComponentImplementation(); } /** * {@inheritDoc} */ public Set<InjectionPoint> getInjectionPoints() { return Collections.emptySet(); } /** * {@inheritDoc} */ public boolean isNullable() { return false; } } }