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 }