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 }