001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.tide.cdi;
023
024import java.io.Serializable;
025import java.lang.annotation.Annotation;
026import java.lang.reflect.ParameterizedType;
027import java.lang.reflect.Type;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.Map;
031import java.util.Set;
032
033import javax.enterprise.context.RequestScoped;
034import javax.enterprise.event.Observes;
035import javax.enterprise.inject.spi.AfterBeanDiscovery;
036import javax.enterprise.inject.spi.AfterDeploymentValidation;
037import javax.enterprise.inject.spi.AnnotatedConstructor;
038import javax.enterprise.inject.spi.AnnotatedField;
039import javax.enterprise.inject.spi.AnnotatedMethod;
040import javax.enterprise.inject.spi.AnnotatedType;
041import javax.enterprise.inject.spi.Bean;
042import javax.enterprise.inject.spi.BeanManager;
043import javax.enterprise.inject.spi.Extension;
044import javax.enterprise.inject.spi.ProcessAnnotatedType;
045import javax.enterprise.inject.spi.ProcessBean;
046import javax.enterprise.inject.spi.ProcessProducerField;
047import javax.enterprise.inject.spi.ProcessProducerMethod;
048import javax.enterprise.util.AnnotationLiteral;
049import javax.inject.Inject;
050import javax.inject.Named;
051
052import org.granite.logging.Logger;
053import org.granite.messaging.amf.io.util.externalizer.annotation.ExternalizedBean;
054import org.granite.messaging.service.annotations.RemoteDestination;
055import org.granite.tide.annotations.TideEnabled;
056
057
058/**
059 * @author William DRAI
060 */
061public class TideExtension implements Extension {
062        
063        private static final Logger log = Logger.getLogger(TideExtension.class);
064        
065        
066        @Inject
067        BeanManager manager;
068
069        private Bean<?> tideInstrumentedBeans = null;
070        private Map<Type, Bean<?>> instrumentedBeans = new HashMap<Type, Bean<?>>();
071        private Map<Object, Type> producedBeans = new HashMap<Object, Type>();
072        
073        
074        public <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> event) {
075                AnnotatedType<X> annotatedType = event.getAnnotatedType();
076                
077                boolean tideComponent = false;
078                boolean tideBean = false;
079                for (Type type : annotatedType.getTypeClosure()) {
080                        if (type instanceof ParameterizedType)
081                                type = ((ParameterizedType)type).getRawType();
082                        if (type instanceof Class<?> 
083                                && (((Class<?>)type).isAnnotationPresent(RemoteDestination.class) 
084                                        || ((Class<?>)type).isAnnotationPresent(TideEnabled.class)
085                                        || ((Class<?>)type).isAnnotationPresent(Named.class)
086                                        || ((Class<?>)type).isAnnotationPresent(RequestScoped.class))) {
087                                tideComponent = true;
088                                break;
089                        }
090                        if (type instanceof Class<?> 
091                                && ((Serializable.class.isAssignableFrom((Class<?>)type) && ((Class<?>)type).isAnnotationPresent(Named.class)) 
092                                        || ((Class<?>)type).isAnnotationPresent(ExternalizedBean.class))) {
093                                tideBean = true;
094                                break;
095                        }
096                }
097                
098                if (tideComponent || tideBean)
099                        event.setAnnotatedType(new TideAnnotatedType<X>(annotatedType, tideComponent, tideBean));
100        }
101        
102        public <X> void processBean(@Observes ProcessBean<X> event) {
103                if (event.getAnnotated().isAnnotationPresent(TideComponent.class) || event.getAnnotated().isAnnotationPresent(TideBean.class)) {
104                        instrumentedBeans.put(event.getAnnotated().getBaseType(), event.getBean());
105                        log.info("Instrumented Tide component %s", event.getBean().toString());
106                }
107                
108                Bean<?> bean = event.getBean();
109                if (event instanceof ProcessProducerMethod<?, ?>) {
110                        Type type = ((ProcessProducerMethod<?, ?>)event).getAnnotatedProducerMethod().getDeclaringType().getBaseType();                   
111                        producedBeans.put(((ProcessProducerMethod<?, ?>)event).getAnnotatedProducerMethod().getBaseType(), type);
112                        if (bean.getName() != null)
113                                producedBeans.put(bean.getName(), type);
114                }
115                else if (event instanceof ProcessProducerField<?, ?>) {
116                        Type type = ((ProcessProducerField<?, ?>)event).getAnnotatedProducerField().getDeclaringType().getBaseType();
117                        producedBeans.put(((ProcessProducerField<?, ?>)event).getAnnotatedProducerField().getBaseType(), type);
118                        if (bean.getName() != null)
119                                producedBeans.put(bean.getName(), type);
120                }
121                
122                if (event.getBean().getBeanClass().equals(TideInstrumentedBeans.class))
123                        tideInstrumentedBeans = event.getBean();
124        }
125        
126        public void processAfterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager manager) {
127        }
128        
129        public void processAfterDeploymentValidation(@Observes AfterDeploymentValidation event, BeanManager manager) {
130        if (tideInstrumentedBeans == null)
131            return;
132        TideInstrumentedBeans ib = (TideInstrumentedBeans)manager.getReference(tideInstrumentedBeans, TideInstrumentedBeans.class,
133                manager.createCreationalContext(tideInstrumentedBeans));
134        ib.setBeans(instrumentedBeans);
135        ib.setProducedBeans(producedBeans);
136        }
137        
138
139        
140        @SuppressWarnings("serial")
141        public static class TideAnnotatedType<T> implements AnnotatedType<T> {
142                
143                private final AnnotatedType<T> annotatedType;
144                private final Annotation componentQualifier = new AnnotationLiteral<TideComponent>() {};
145                private final Annotation beanQualifier = new AnnotationLiteral<TideBean>() {};
146                private final Set<Annotation> annotations;
147                
148                
149                public TideAnnotatedType(AnnotatedType<T> annotatedType, boolean component, boolean bean) {
150                        this.annotatedType = annotatedType;
151                        annotations = new HashSet<Annotation>(annotatedType.getAnnotations());
152                        if (component)
153                                annotations.add(componentQualifier);
154                        if (bean)
155                                annotations.add(beanQualifier);
156                }
157
158                public Set<AnnotatedConstructor<T>> getConstructors() {
159                        return annotatedType.getConstructors();
160                }
161
162                public Set<AnnotatedField<? super T>> getFields() {
163                        return annotatedType.getFields();
164                }
165
166                public Class<T> getJavaClass() {
167                        return annotatedType.getJavaClass();
168                }
169
170                public Set<AnnotatedMethod<? super T>> getMethods() {
171                        return annotatedType.getMethods();
172                }
173
174                @SuppressWarnings("unchecked")
175                public <X extends Annotation> X getAnnotation(Class<X> annotationClass) {
176                        if (annotationClass.equals(TideComponent.class))
177                                return (X)componentQualifier;
178                        if (annotationClass.equals(TideBean.class))
179                                return (X)beanQualifier;
180                        return annotatedType.getAnnotation(annotationClass);
181                }
182
183                public Set<Annotation> getAnnotations() {
184                        return annotations;
185                }
186
187                public Type getBaseType() {
188                        return annotatedType.getBaseType();
189                }
190
191                public Set<Type> getTypeClosure() {
192                        return annotatedType.getTypeClosure();
193                }
194
195                public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
196                        if (annotationClass.equals(TideComponent.class) && annotations.contains(componentQualifier))
197                                return true;                    
198                        if (annotationClass.equals(TideBean.class) && annotations.contains(beanQualifier))
199                                return true;                    
200                        return annotatedType.isAnnotationPresent(annotationClass);
201                }
202        }
203}