001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.tide.cdi;
022    
023    import java.io.Serializable;
024    import java.lang.reflect.Method;
025    import java.util.ArrayList;
026    import java.util.List;
027    import java.util.Map.Entry;
028    
029    import javax.enterprise.inject.spi.Bean;
030    import javax.inject.Inject;
031    import javax.interceptor.AroundInvoke;
032    import javax.interceptor.Interceptor;
033    import javax.interceptor.InvocationContext;
034    
035    import org.granite.logging.Logger;
036    import org.granite.tide.invocation.ContextResult;
037    import org.granite.tide.invocation.ContextUpdate;
038    import org.granite.util.Reflections;
039    
040    
041    @TideComponent @Interceptor
042    public class TideComponentInterceptor implements Serializable {
043            
044            private static final long serialVersionUID = 1L;
045    
046            private static final Logger log = Logger.getLogger(TideComponentInterceptor.class);
047            
048        private boolean reentrant;  // CDI components are single threaded
049            
050            @Inject
051            private CDIServiceContext tideContext;
052            
053            @Inject
054            private TideInstrumentedBeans instrumentedBeans;
055            
056    
057            @SuppressWarnings("unchecked")
058            @AroundInvoke
059            public Object doAround(InvocationContext invocation) throws Exception {
060                    
061            if (reentrant)
062                    return invocation.proceed();
063            
064            TideInvocation tideInvocation = TideInvocation.get();
065            
066            if (tideContext == null || tideInvocation == null || tideInvocation.isLocked())
067                return invocation.proceed();
068            
069            try {
070                    reentrant = true;
071                    
072                    boolean evaluate = false;
073                    
074                    if (tideInvocation.isEnabled() && !tideInvocation.isUpdated()) {
075                        List<ContextUpdate> updates = new ArrayList<ContextUpdate>(tideInvocation.getUpdates());
076                        tideInvocation.updated();
077                        tideContext.restoreContext(updates, invocation.getTarget());
078                        evaluate = true;
079                    }
080                    
081                            List<Object[]> crs = new ArrayList<Object[]>();             
082                    Bean<?> bean = instrumentedBeans.getBean(invocation.getTarget().getClass());
083                    if (bean == null)
084                            log.warn("Instrumented Bean not found for class " + invocation.getTarget().getClass());
085                    else {
086                                    String componentName = bean.getName();
087                                    
088                                    for (Entry<ContextResult, Boolean> me : tideContext.getResultsEval().entrySet()) {
089                                            ContextResult cr = me.getKey();
090                                            if (cr.getExpression() == null 
091                                                            || !((cr.getComponentName() != null && cr.getComponentName().equals(componentName))
092                                                            || (cr.getComponentClassName() != null && bean.getTypes().contains(cr.getComponentClass()))))
093                                                    continue;
094                                            int idx = cr.getExpression().indexOf('.');
095                                            String propName = idx >= 0 ? cr.getExpression().substring(0, idx) : cr.getExpression();
096                                            Method getter = null;
097                                            try {
098                                                    getter = Reflections.getGetterMethod(invocation.getTarget().getClass(), propName);
099                                            }
100                                            catch (Exception e) {
101                                            }
102                                            if (getter != null)
103                                                    crs.add(new Object[] { me, getter, getter.invoke(invocation.getTarget()) });
104                                    }
105                    }
106                            
107                            Object result = invocation.proceed();
108                            
109                            for (Object[] cr : crs) {
110                                    Method getter = (Method)cr[1];
111                                    Object newValue = getter.invoke(invocation.getTarget());
112                                    if ((newValue == null && cr[2] != null)
113                                            || (newValue != null && !newValue.equals(cr[2])))
114                                                    ((Entry<ContextResult, Boolean>)cr[0]).setValue(true);
115                            }
116                            
117                            for (Entry<ContextResult, Boolean> me : tideContext.getResultsEval().entrySet()) {
118                                    ContextResult cr = me.getKey();
119                                    if (cr.getExpression() != null)
120                                            continue;
121                                    
122                                    if (cr.getComponentClassName() != null && instrumentedBeans.isProducedBy(cr.getComponentClass(), invocation.getTarget().getClass()))
123                                            me.setValue(true);
124                                    
125                                    if (cr.getComponentName() != null && instrumentedBeans.isProducedBy(cr.getComponentName(), invocation.getTarget().getClass()))
126                                            me.setValue(true);
127                            }
128                            
129                    if (evaluate)
130                        tideInvocation.evaluated(tideContext.evaluateResults(invocation.getTarget(), false));
131            
132                            return result;
133            }
134            finally {
135                    reentrant = false;
136            }
137            }
138    }