/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.richfaces.cdi.push; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.event.Reception; import javax.enterprise.event.TransactionPhase; 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.ObserverMethod; import javax.enterprise.inject.spi.ProcessInjectionTarget; import org.richfaces.application.push.MessageException; import org.richfaces.application.push.TopicKey; import org.richfaces.application.push.TopicsContext; /** * <p> * CDI Extension for observing CDI events and delegating events with their payload to Push message bus. * </p> * * <p> * This extension was introduced as workaround for feature missing in CDI 1.0 (<a * href="https://issues.jboss.org/browse/CDI-36">CDI-36</a>). * </p> * * <p> * Thus this extension listens on injection target scanning process for all {@link Push} annotations defined on injection points * and then registers observer methods designed for one specific topic. As consequence is that this implementation can't be used * for observing dynamically created topic names. * </p> * * @author <a href="http://community.jboss.org/people/lfryc">Lukas Fryc</a> */ public class PushCDIExtension implements Extension { private Set<Push> pushAnnotations = new LinkedHashSet<Push>(); /** * Scans all the injection points on found injection targets for {@link Push} annotations */ public void processInjectionTarget(@Observes ProcessInjectionTarget<?> pit) { scanForPushAnnotations(pit.getInjectionTarget().getInjectionPoints()); } /** * Stores all {@link Push} annotations on injection points annotated by {@link Push} * * @param injectionPoints injection points to be scanned */ private void scanForPushAnnotations(Collection<InjectionPoint> injectionPoints) { for (InjectionPoint injectionPoint : injectionPoints) { Push pushAnnotation = injectionPoint.getAnnotated().getAnnotation(Push.class); if (pushAnnotation != null) { pushAnnotations.add(pushAnnotation); } } } /** * Register observer method {@link PushObserverMethod} for each {@link Push} annotation found in annotation scanning. * * @param event * @param beanManager */ public void afterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager beanManager) { for (Push pushAnnotation : pushAnnotations) { event.addObserverMethod(new PushObserverMethod(beanManager, pushAnnotation)); } } /** * <p> * This class remembers {@link BeanManager} and {@link Push} annotation. * </p> * * <p> * {@link BeanManager} is needed to get reference to {@link TopicKeyResolver} and {@link TopicsContext}. * </p> * * <p> * {@link TopicKeyResolver} is then used to resolve {@link TopicKey} from {@link Push} annotation. * </p> * * <p> * {@link TopicsContext} is used for publishing into RichFaces Push message bus. * </p> * * @author lfryc * */ private static class PushObserverMethod implements ObserverMethod<Object> { private BeanManager beanManager; private Push pushAnnotation; public PushObserverMethod(BeanManager beanManager, Push pushAnnotation) { this.beanManager = beanManager; this.pushAnnotation = pushAnnotation; } /** * Publishes message to topic determined by {@link TopicKey} using {@link TopicsContext}. * * References to {@link TopicsContext} and {@link TopicKeyResolver} (needed for {@link TopicKey} resolution) are done * through {@link BeanManager}. */ public void notify(Object payload) { TopicsContext topicsContext = getBeanReference(TopicsContext.class); TopicKeyResolver topicKeyResolver = getBeanReference(TopicKeyResolver.class); TopicKey topicKey = topicKeyResolver.resolveTopicKey(pushAnnotation); try { topicsContext.publish(topicKey, payload); } catch (MessageException e) { throw new PushCDIMessageException("Message wasn't successfully deliver", e); } } public Class<?> getBeanClass() { return PushObserverMethod.class; } public Type getObservedType() { return Object.class; } public Set<Annotation> getObservedQualifiers() { return new HashSet<Annotation>(Arrays.asList(pushAnnotation)); } public Reception getReception() { return Reception.IF_EXISTS; } public TransactionPhase getTransactionPhase() { return TransactionPhase.IN_PROGRESS; } private <T> T getBeanReference(Class<T> beanType) { Set<Bean<?>> beans = beanManager.getBeans(beanType); Bean<?> bean = (Bean<?>) beans.iterator().next(); CreationalContext<?> ctx = beanManager.createCreationalContext(bean); @SuppressWarnings("unchecked") T reference = (T) beanManager.getReference(bean, beanType, ctx); return reference; } } }