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     */
022    package org.granite.tide.cdi;
023    
024    import java.io.Serializable;
025    import java.lang.reflect.Method;
026    import java.util.ArrayList;
027    import java.util.List;
028    import java.util.Map.Entry;
029    
030    import javax.enterprise.inject.spi.Bean;
031    import javax.inject.Inject;
032    import javax.interceptor.AroundInvoke;
033    import javax.interceptor.Interceptor;
034    import javax.interceptor.InvocationContext;
035    
036    import org.granite.logging.Logger;
037    import org.granite.tide.invocation.ContextResult;
038    import org.granite.tide.invocation.ContextUpdate;
039    import org.granite.util.Reflections;
040    
041    
042    @TideComponent @Interceptor
043    public class TideComponentInterceptor implements Serializable {
044            
045            private static final long serialVersionUID = 1L;
046    
047            private static final Logger log = Logger.getLogger(TideComponentInterceptor.class);
048            
049        private boolean reentrant;  // CDI components are single threaded
050            
051            @Inject
052            private CDIServiceContext tideContext;
053            
054            @Inject
055            private TideInstrumentedBeans instrumentedBeans;
056            
057    
058            @SuppressWarnings("unchecked")
059            @AroundInvoke
060            public Object doAround(InvocationContext invocation) throws Exception {
061                    
062            if (reentrant)
063                    return invocation.proceed();
064            
065            TideInvocation tideInvocation = TideInvocation.get();
066            
067            if (tideContext == null || tideInvocation == null || tideInvocation.isLocked())
068                return invocation.proceed();
069            
070            try {
071                    reentrant = true;
072                    
073                    boolean evaluate = false;
074                    
075                    if (tideInvocation.isEnabled() && !tideInvocation.isUpdated()) {
076                        List<ContextUpdate> updates = new ArrayList<ContextUpdate>(tideInvocation.getUpdates());
077                        tideInvocation.updated();
078                        tideContext.restoreContext(updates, invocation.getTarget());
079                        evaluate = true;
080                    }
081                    
082                            List<Object[]> crs = new ArrayList<Object[]>();             
083                    Bean<?> bean = instrumentedBeans.getBean(invocation.getTarget().getClass());
084                    if (bean == null)
085                            log.warn("Instrumented Bean not found for class " + invocation.getTarget().getClass());
086                    else {
087                                    String componentName = bean.getName();
088                                    
089                                    for (Entry<ContextResult, Boolean> me : tideContext.getResultsEval().entrySet()) {
090                                            ContextResult cr = me.getKey();
091                                            if (cr.getExpression() == null 
092                                                            || !((cr.getComponentName() != null && cr.getComponentName().equals(componentName))
093                                                            || (cr.getComponentClassName() != null && bean.getTypes().contains(cr.getComponentClass()))))
094                                                    continue;
095                                            int idx = cr.getExpression().indexOf('.');
096                                            String propName = idx >= 0 ? cr.getExpression().substring(0, idx) : cr.getExpression();
097                                            Method getter = null;
098                                            try {
099                                                    getter = Reflections.getGetterMethod(invocation.getTarget().getClass(), propName);
100                                            }
101                                            catch (Exception e) {
102                                            }
103                                            if (getter != null)
104                                                    crs.add(new Object[] { me, getter, getter.invoke(invocation.getTarget()) });
105                                    }
106                    }
107                            
108                            Object result = invocation.proceed();
109                            
110                            for (Object[] cr : crs) {
111                                    Method getter = (Method)cr[1];
112                                    Object newValue = getter.invoke(invocation.getTarget());
113                                    if ((newValue == null && cr[2] != null)
114                                            || (newValue != null && !newValue.equals(cr[2])))
115                                                    ((Entry<ContextResult, Boolean>)cr[0]).setValue(true);
116                            }
117                            
118                            for (Entry<ContextResult, Boolean> me : tideContext.getResultsEval().entrySet()) {
119                                    ContextResult cr = me.getKey();
120                                    if (cr.getExpression() != null)
121                                            continue;
122                                    
123                                    if (cr.getComponentClassName() != null && instrumentedBeans.isProducedBy(cr.getComponentClass(), invocation.getTarget().getClass()))
124                                            me.setValue(true);
125                                    
126                                    if (cr.getComponentName() != null && instrumentedBeans.isProducedBy(cr.getComponentName(), invocation.getTarget().getClass()))
127                                            me.setValue(true);
128                            }
129                            
130                    if (evaluate)
131                        tideInvocation.evaluated(tideContext.evaluateResults(invocation.getTarget(), false));
132            
133                            return result;
134            }
135            finally {
136                    reentrant = false;
137            }
138            }
139    }